tarjan求edcc vdcc
//求无向图:e-dcc(边双联通分量) //重边的处理 e---e反
//low[x]>dfn[y],edge(x,y)才可以
//键图:belong[point]=edcc struct node { int fr,to,nxt; }e[N<<1]; int tot=1,head[N]; inline void add(int x,int y) { e[++tot].to=y,e[tot].nxt=head[x],head[x]=tot; e[tot].fr=x; } int n,m; int dfn[N],dfn_cnt,belong[N],low[N],edcc; bool cut[N<<1]; inline void tarjan(int x,int lst_edge) { dfn[x]=low[x]=++dfn_cnt; for(int i=head[x];i;i=e[i].nxt) { int to=e[i].to; if(!dfn[to]) { tarjan(to,i); low[x]=min(low[x],low[to]); if(low[to]>dfn[x]) { cut[i]=cut[i^1]=1; } } else if(i!=(lst_edge^1))low[x]=min(low[x],dfn[to]); } } inline void dfs(int x) { belong[x]=edcc; for(int i=head[x];i;i=e[i].nxt) { int to=e[i].to; if(cut[i]||belong[to])continue; //这里belong!=0就不行,因为我只是要找哪个点是哪个edcc,访问过了就没有必要访问了 dfs(to); } } int main() { // freopen("exam.txt","r",stdin); // freopen("a.out","w",stdout); n=re(),m=re(); _f(i,1,m) { int u=re(),v=re(); add(u,v);add(v,u); } _f(i,1,n)if(!dfn[i])tarjan(i); _f(i,1,n) { if(!belong[i])edcc++,dfs(i); }//找出那个点属于哪些edcc _f(i,2,tot) { if(belong[e[i].fr]!=belong[e[i].to])Add(e[i].fr,e[i].to);//这就是新的图了 } return 0; }
//求无向图:-dcc(点双联通分量) //根节点必2个special point (o--o) dcc,x要留着 只要有,就放 //low[y]>=dfn[x]
//我每次判断找dcc时都是截止到这个儿子,所以每次tarjan一个儿子都要找一遍dcc
//如果有自环的话记得去掉否则单点算不上
struct node { int fr,to,nxt; }e[N<<1]; int tot=1,head[N]; inline void add(int x,int y) { e[++tot].to=y,e[tot].nxt=head[x],head[x]=tot; e[tot].fr=x; } int n,m; int dfn[N],dfn_cnt,belong[N],low[N],edcc; vector<int>dcc[N]; int cnt;bool cut[N]; int stack[N],top; int root; void tarjan(int x) { dfn[x]=low[x]=++dfn_cnt; stack[++top]=x; if(x==root&&head[x]==0) { dcc[++cnt].push_back(x);return ; } int f=0; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); if(low[y]>=dfn[x]) { f++; if(f>1||x!=root)cut[x]=true; cnt++; int z=0; do { z=stack[top--]; dcc[cnt].push_back(z); }while(z!=y); dcc[cnt].push_back(x);//但是x还要留在stack里面 } } else low[x]=min(low[x],dfn[y]); } }int num; int main() { // freopen("exam.txt","r",stdin); // freopen("a.out","w",stdout); n=re(),m=re(); _f(i,1,m) { int u=re(),v=re(); add(u,v);add(v,u); } _f(i,1,n)if(!dfn[i])root=i,tarjan(i); num=cnt; for(int i=1;i<=cnt;i++) { if(cut[i])new_id[i]=++num; } tc=1; for(int i=1;i<=cnt;i++) { for(int j=0;j<dcc[i].size();j++) { int x=dcc[i][j]; if(cut[x]) { add_c(i,new_id[x]); add_c[new_id[x],i); } else c[x]=i; } } return 0; }
//求有向图:-(强双联通分量) //在求scc时我要遍历完x的所有子节点之后才能找,因为这 //不是单独的一个环,试想所有环都套在一块那也是scc //low[y]==dfn[x],必须是仍然在队里面 struct node { int fr,to,nxt; }e[N<<1]; int tot=1,head[N]; inline void add(int x,int y) { e[++tot].to=y,e[tot].nxt=head[x],head[x]=tot; e[tot].fr=x; } int dfn[N],dfn_cnt,low[N]; int stack[N],top; bool in[N]; vector<int>scc[N]; int cnt; inline void tarjan(int x) { dfn[x]=low[x]=++dfn_cnt; stack[++top]=x; for(int i=head[x];i;i=e[i].nxt) { int to=e[i].to; if(!dfn[to]) { tarjan(to); low[x]=min(low[x],low[to]); } else if(in[to])low[x]=min(dfn[to],low[x]); } if(dfn[x]==low[x]) { cnt++; int y; do { y=stack[top--]; scc[cnt].push_back(y);ins[y]=0;c[y]=cnt; }while(y!=x) } } int main() { // freopen("exam.txt","r",stdin); // freopen("a.out","w",stdout); n=re(),m=re(); for(int i=1;i<=m;i++) { int x=re(),y=re();add(x,y); } _f(i,1,n)if(!dfn[i])tarjan(i); _f(i,2,tot) { if(c[e[i].fr]==c[e[i].to])continue; Add(e[i].fr,e[i].to); } return 0; }