Codeforces962F Simple Cycles Edges 【双连通分量】【dfs树】
题目大意:
给出一个无向图,问有哪些边只属于一个简单环。
题目分析:
如果这道题我们掌握了点双连通分量,那么结论会很显然,找到每个点双,如果一个n个点的点双正好由n条边构成,那么这些边都是可以的。
这样想显得很没有技术含量,使用一类通用的做法做一些有特点的题目总是不那么锻炼人的思维,但在算法竞赛中我仍然推荐点双的做法。
这题很有特点,我们尝试不用点双解决它。
首先,考虑一个简单环,它不由几个简单环组合并删去某些边组合而成。它的dfs树的形状将会是这样的:
其中箭头标注的是返祖边。
一个简单环中的所有边被选,当且仅当它唯一的一条返祖边所包括的点不被其它返祖边所覆盖任何一段,但是这一段并不包含仅包含一个点的情况。
所以我们可以考虑类似运输计划那样的差分。然后前缀和维护经过的返祖边大于2的边数,这样对每条返祖边判断是O(1)的。所以时间复杂度O(n+m+sort(m))。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 102000; 5 6 #define mp make_pair 7 8 int n,m; 9 10 struct edge{int from,to;}e[maxn]; 11 vector<pair<int,int> > g[maxn]; 12 int dfn[maxn],f[maxn],dd[maxn],cl,pt[maxn]; 13 int chs[maxn],lnk[maxn]; 14 15 void read(){ 16 scanf("%d%d",&n,&m); 17 for(int i=1;i<=m;i++){ 18 scanf("%d%d",&e[i].from,&e[i].to); 19 g[e[i].from].push_back(mp(e[i].to,i)); 20 g[e[i].to].push_back(mp(e[i].from,i)); 21 } 22 } 23 24 vector<int> ans; 25 vector<int> t[maxn]; 26 27 void dfs(int now,int fa){ 28 dfn[now] = ++cl;pt[now] = fa; 29 for(int i=0;i<g[now].size();i++){ 30 pair<int,int> pr = g[now][i]; 31 if(pr.first == fa) continue; 32 if(!dfn[pr.first]){ 33 t[now].push_back(pr.first); 34 chs[pr.second] = 1;lnk[pr.first] = pr.second; 35 dfs(pr.first,now); 36 } 37 else{ 38 if(dfn[pr.first] > dfn[now]) continue; 39 f[now]++; f[pr.first]--; 40 } 41 } 42 } 43 44 int im[maxn]; 45 void dfs2(int now){ 46 for(int i=0;i<t[now].size();i++){dfs2(t[now][i]); dd[now]+=dd[t[now][i]];} 47 dd[now] += f[now]; 48 } 49 void dfs3(int now){ 50 im[now] = im[pt[now]] + (dd[now] > 1); 51 for(int i=0;i<t[now].size();i++){dfs3(t[now][i]);} 52 } 53 54 void solve(int now){ 55 if(dfn[e[now].from] < dfn[e[now].to]) swap(e[now].from,e[now].to); 56 int kk = im[e[now].from] - im[e[now].to]; 57 if(kk == 0){ 58 int pla = e[now].from; 59 while(pla!=e[now].to){ans.push_back(lnk[pla]);pla = pt[pla];} 60 ans.push_back(now); 61 } 62 } 63 64 void work(){ 65 for(int i=1;i<=n;i++) { if(!dfn[i]) dfs(i,0),dfs2(i),dfs3(i); } 66 for(int i=1;i<=m;i++){ if(!chs[i]) solve(i); } 67 sort(ans.begin(),ans.end()); 68 printf("%d\n",ans.size()); 69 for(int i=0;i<ans.size();i++){ 70 printf("%d ",ans[i]); 71 } 72 } 73 74 int main(){ 75 //freopen("1.in","r",stdin); 76 //freopen("1.out","w",stdout); 77 read(); 78 work(); 79 return 0; 80 }