2 条题解
-
1
走出的回路一定在 scc 中,因此使用 tarjan 求出 scc。
复杂度困境
从每一个点都做一遍 bfs 会超时。
期望只从 scc 的一个点出发做 bfs,即可判断 scc 中的所有点。
破局:“以点代面”
发现如下性质。 从 scc 内的任意一点 可以走出一条合法的回路 scc 内所有点都可以走出一条合法的回路。
充分性: 存在一条从 回到 的合法回路,回路长度 。 从任意一个其他点 可以首先走到这个回路上,设路径长度为 ;在这个回路上绕 圈,路径长度为 ;最后离开回路,回到点 ,设路径长度为 。 从 到 的回路合法,等价于 。 可以发现,不管 在模 3 意义下为多少,都可以改变 的值使等式成立。充分性得证。
必要性:由命题直接得到。
我们从每个 scc 的任意点做分层图 bfs,判断这个点是否可以走一条合法的回路。如果可以,则将答案加上当前 scc 的大小。
代码实现
#include<bits/stdc++.h> using namespace std; typedef pair<int, int> pii; const int N = 2e5 + 10, M = 4e5 + 10; struct Edge{ int to, nxt; }edge[M]; int n, m; int head[N], tot; int nowdfn, dfn[N], low[N], stk[N], tp; bool instk[N]; int color[N], nowscc; vector<int> scc[N]; bool vis[N][3]; void add(int u, int v){ edge[++tot].to = v; edge[tot].nxt = head[u]; head[u] = tot; } void tarjan(int u){ dfn[u] = low[u] = ++nowdfn; stk[++tp] = u, instk[u] = 1; for(int i = head[u]; i; i = edge[i].nxt){ int v = edge[i].to; if(!dfn[v]){ tarjan(v); low[u] = min(low[u], low[v]); }else if(instk[v]){//ancestor low[u] = min(low[u], dfn[v]); } } if(dfn[u] == low[u]){ ++nowscc; int p = 0; do{ p = stk[tp--]; scc[nowscc].push_back(p); instk[p] = 0; color[p] = nowscc; }while(tp!=0 && p != u); } } void bfs(int st){ queue<pii> q; q.push({st, 0}); vis[st][0] = 1; while(!q.empty()){ int u = q.front().first, r = q.front().second; q.pop(); for(int i = head[u]; i; i = edge[i].nxt){ int v = edge[i].to; if(!vis[v][(r+1)%3]){ vis[v][(r+1)%3] = 1; q.push({v, (r+1)%3}); } } } } int main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin >> n >> m; for(int i = 1, u, v; i <= m; i ++){ cin >> u >> v; add(u, v); } for(int i = 1; i <= n; i ++){ if(!dfn[i]) tarjan(i); } int ans = 0; for(int i = 1; i <= nowscc; i ++){//scc bfs(scc[i][0]); if(vis[scc[i][0]][1]) ans += scc[i].size(); } cout << ans; return 0; }
信息
- ID
- 2772
- 时间
- 2000ms
- 内存
- 128MiB
- 难度
- 6
- 标签
- (无)
- 递交数
- 41
- 已通过
- 8
- 上传者