ARC111-D Orientation
これも解けるべきだったなと思う 解法はほぼeditorialそのまま
問題概要
https://atcoder.jp/contests/arc111/tasks/arc111_d
無向グラフが与えられる。次の条件を満たすようにグラフの各辺を向きづけよ。
- 頂点 i から到達可能な頂点数が(自分も含めて)ちょうど C_i 個存在する。
constraints
- 1 ≤ |V| ≤ 100
解法
各辺について、両端の頂点をa , bとして、C_a != C_bだったら明らかである。C_a > C_bの時、bから到達できる頂点には必ず a から到達できるため、a -> bの方向に向きづける。
C_a == C_b の場合。C_aと同じ連結成分内にあって、Cの値が同じ頂点は、「強連結」であることがわかる。ある頂点集合が強連結であるとは、その頂点集合内の任意の2点について、2点を結ぶパスが存在することである。さて、問題は無向頂点集合を向きづけて強連結にすることになったが、これは公式解説にも概要があるが
ここに詳しく説明されている。これ読んだことあってライブラリまで作ってたのにな...
- 結論としては、DFS木に含まれる辺(span-edge)については「潜る」方向に、そうで無い辺(back-edge)については「遡る」方向に向きづければ良い。
#include<bits/stdc++.h> using namespace std; #define LIM 110 int N,M; pair<int,int> edge[LIM * LIM * 2]; int g[LIM][LIM]; int c[LIM]; int visited[LIM],depth[LIM]; vector<int> graph[LIM]; bool dfstree[LIM][LIM]; void dfs(int x,int pre,int dpt){ visited[x] = 1; depth[x] = dpt; for(auto e : graph[x]){ if(e != pre && !visited[e]){ dfstree[x][e] = dfstree[e][x] = true; dfs(e,x,dpt+1); } } } int main(){ cin.tie(0); ios::sync_with_stdio(false); cin >> N >> M; for(int i = 0;i < M;i++){ int a,b;cin >> a >> b; a--,b--; edge[i] = make_pair(a,b); g[a][b] = g[b][a] = 1; } for(int i = 0;i < N;i++){ cin >> c[i]; } // c[i] == c[j]の辺を無向辺で結ぶ for(int i = 0;i < N;i++){ for(int j = 0;j < N;j++){ if(g[i][j] && (c[i] == c[j])){ graph[i].push_back(j); } } } for(int i = 0;i < N;i++){ if(!visited[i]){ dfs(i,-1,0); } } for(int i = 0;i < M;i++){ auto[a,b] = edge[i]; if(c[a] > c[b]){ cout << "->" << '\n'; }else if(c[a] == c[b]){ if(dfstree[a][b]){ cout << (depth[a] < depth[b] ? "->" : "<-") << '\n'; }else{ cout << (depth[a] > depth[b] ? "->" : "<-") << '\n'; } }else{ cout << "<-" << '\n'; } } }