$\newcommand{\O}{\mathrm{O}}$
Functional Graph とは各頂点の出次数がちょうど $1$ の単純な有向グラフのことである. グラフが $\{ 1, \ldots ,n \} \rightarrow \{ 1, \ldots ,n \}$ の関数として表現できることがその名の由来であると思われる.
定義よりグラフはループと(無向グラフと見たとき)ループ上の頂点を根とする木からなるグラフになる.
(関数)
solve(): ループ上の頂点を $loop$ の配列に入れる.
make_graph(): (無向グラフと見たとき)ループ上の頂点を根とする木たちをループ側から端に向かう方向(元とは逆の方向)の有向森とみたグラフを構築する.
時間計算量: $\O (n)$
// すべての頂点の出次数が 1 のグラフ // loop に辺に沿ったループ上の頂点が入る // visit[v] は頂点 v が i(0_indexed) 番目のループ上の頂点なら i+1 (≧ 1), // v がループ上になく, i 番目のループにたどり着く場合 -(i+1) (≦ -1) が格納される. // make_graph() でループから葉へ向かう方向の森を構築 class FunctionalGraph { private: const int V; int loop_id; vector<int> nx; void make_loop(const int st, int nw, vector<int>& vec){ while(nx[nw] != st){ vec.push_back(nx[nw]); visit[nx[nw]] = loop_id; nw = nx[nw]; } } int dfs(int u, vector<int>& vec){ visit[u] = -loop_id; int v = nx[u]; if(visit[v] == -loop_id){ vec.push_back(u), vec.push_back(v); visit[u] = visit[v] = loop_id; make_loop(u, v, vec); loop_id++; return 0; }else if(visit[v] == 0){ const int res = dfs(v, vec); if(res == 0) return 0; else return visit[u] = res; } return visit[u] = (visit[v] > 0 ? -visit[v] : visit[v]); } void make_graph(){ graph.resize(V); for(int i = 0; i < V; i++){ if(visit[i] < 0) graph[nx[i]].push_back(i); } } public: vector<int> visit; vector<vector<int> > loop, graph; FunctionalGraph(const int node_size) : V(node_size), loop_id(1), nx(V, 0), visit(V, 0){} void add_edge(int u, int v){ nx[u] = v; if(u == nx[u]) visit[u] = loop_id++, loop.push_back({u}); } void solve(){ for(int i = 0; i < V; i++){ if(visit[i] == 0){ vector<int> vec; dfs(i, vec); if(!vec.empty()) loop.push_back(move(vec)); } } // make_graph(); } };
JOI : 報告(report) 提出コード