小结:
1,tarjan求有向图强联通请注意处理回退边时,只能把还在栈中元素更新。
2,有重边时,注意不要传father,要传非相邻边。
3,
A - Network of Schools
两个问题:有向图,最小点覆盖,最小边使得图联通。
最小点覆盖 :即为入度为0强联通个数。
最小边 :
图中入度为0的强联通数为 a ,出度为0的强联通为 b 。那么让出度为0 分量去 连 入度为 0 的分量一定是最优的,
#include<cstdio> #include<iostream> #include<stack> #include<cstring> using namespace std; typedef long long ll; // const ll MOD=998244353; const int N=1e5+5; int dfn,n,ecnt,SCC; int sccno[N],num[N],low[N],belong[N],in[N],out[N],head[N]; bool instack[N]; struct edge{int v,next;}e[N]; void add(int u,int v){ e[ecnt].v=v;e[ecnt].next=head[u];head[u]=ecnt++; } stack<int>sta; void init(){ ecnt=SCC=dfn=0; memset(head,-1,sizeof head); memset(in,0,sizeof in); memset(out,0,sizeof out); memset(belong,0,sizeof belong); memset(low,0,sizeof low); memset(num,0,sizeof num); memset(sccno,0,sizeof sccno); while(!sta.empty())sta.pop(); } void tarjan(int u){ num[u]=low[u]=++dfn; sta.push(u); for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(!num[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if(!belong[v])low[u]=min(low[u],num[v]); } if(num[u]==low[u]){ SCC++; while(1){ int v=sta.top();sta.pop(); belong[v]=SCC; if(v==u)break; } } } void solve(){ for(int i=1;i<=n;i++)if(!num[i])tarjan(i); for(int u=1;u<=n;u++){ for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(belong[u]!=belong[v]){ out[ belong[u] ]++; in[ belong[v] ]++; } } } int a=0,b=0; for(int i=1;i<=SCC;i++){ if(in[i]==0)a++; else if(out[i]==0)b++; } if(SCC==1)cout<<1<<endl<<0<<endl; else cout<<a<<endl<<max(a,b)<<endl; } int main(){ init(); scanf("%d",&n); for(int u=1,v;u<=n;u++){ while(~scanf("%d",&v),v){ add(u,v); } } solve(); system("pause"); return 0; }
D - Network
动态求SCC个数。
对于一张图,跑一遍tarjan,生成一颗树,树边一定是割边,当新加入边时,那么 u ,v 到 lca 的边都变成非桥,暴力更新即可。
听说压点更快,少了在一个强联通的时间。
#include<cstdio> #include<iostream> #include<stack> #include<cstring> using namespace std; typedef long long ll; const ll MOD=998244353; const int N=1e5+5; int dfn,n,m,ecnt,SCC; struct edge{int v,next;}e[N*10]; int head[N],low[N],num[N],dep[N],bridge[N],pre[N]; void init(){ memset(head,-1,sizeof head); memset(low,0,sizeof low); memset(num,0,sizeof num); memset(dep,0,sizeof dep); memset(bridge,0,sizeof bridge); memset(pre,0,sizeof pre); SCC=ecnt=dfn=0; } void add(int u,int v){ e[ecnt].v=v;e[ecnt].next=head[u];head[u]=ecnt++; } void tarjan(int u,int fa){ low[u]=num[u]=++dfn; for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(v==fa)continue; if(!num[v]){ dep[v]=dep[u]+1; pre[v]=u; tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>num[u])bridge[v]=1,SCC++; } else low[u]=min(low[u],num[v]); } } void work(int u,int v){ while(dep[u]>dep[v]){ if(bridge[u]){bridge[u]=0,SCC--;} u=pre[u]; } while(dep[u]<dep[v]){ if(bridge[v]){bridge[v]=0,SCC--;} v=pre[v]; } while(u!=v){ if(bridge[u]){SCC--;bridge[u]=0;} if(bridge[v]){SCC--;bridge[v]=0;} u=pre[u]; v=pre[v]; } printf("%d ",SCC); } int main(){ int kase=0; while(~scanf("%d %d",&n,&m),n+m){ init(); for(int i=1,u,v;i<=m;i++){ scanf("%d %d",&u,&v); add(u,v);add(v,u); } tarjan(1,-1); int u,v,q;scanf("%d",&q); if(kase)puts(""); printf("Case %d: ",++kase); while(q--){ scanf("%d %d",&u,&v); // add(u,v);add(v,u); work(u,v); } } // system("pause"); return 0; }
E - Redundant Paths
题意:给你一张图,新增加一些边,使得图成为边双联通,但是图有重边,
解法:tarjan一遍,注意处理回退边时只能传相邻边,传father就gg ,因为有重边,
然后统计叶子节点个数,连边解决。
#include<cstdio> #include<iostream> #include<stack> #include<cstring> using namespace std; typedef long long ll; const ll MOD=998244353; const int N=1e5+5; int top=0,dfn,n,m,ecnt,SCC; struct edge{int v,next;}e[N*10]; int head[N],low[N],num[N],out[N],in[N],belong[N]; // stack<int>stk; int stk[N]; void init(){ memset(head,-1,sizeof head); memset(low,0,sizeof low); memset(num,0,sizeof num); memset(belong,0,sizeof belong); memset(in,0,sizeof in); memset(out,0,sizeof out); SCC=ecnt=dfn=top=0; // while(!stk.empty())stk.pop(); } void add(int u,int v){ e[ecnt].v=v;e[ecnt].next=head[u];head[u]=ecnt++; } void tarjan(int u,int fa){ num[u]=low[u]=++dfn; // stk.push(u); stk[++top]=u; for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if( (i^1) ==fa)continue; if(!num[v]){ tarjan(v,i); low[u]=min(low[u],low[v]); } else if(!belong[v])low[u]=min(low[u],num[v]); } if(num[u]==low[u]){ SCC++; while(1){ int v=stk[top];top--; // int v=stk.top();stk.pop(); belong[v]=SCC; if(v==u)break; } } } int main(){ // while(scanf("%d %d",&n,&m)){ scanf("%d %d",&n,&m); init(); for(int i=1,u,v;i<=m;i++){ scanf("%d %d",&u,&v); add(u,v);add(v,u); } for(int i=1;i<=n;i++)if(!num[i])tarjan(i,-1); for(int u=1;u<=n;u++){ for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(belong[u]!=belong[v]){ out[ belong[u] ]++; in[ belong[v] ]++; } } } int ans=0; for(int i=1;i<=SCC;i++)if(in[i]==1)ans++; ans=(ans+1)/2; printf("%d ",ans); // } system("pause"); return 0; } // 题意:给你一张图,新增加一些边,使得图成为边双联通,但是图有重边, // 解法:tarjan一遍,注意处理回退边时只能传相邻边,传father就gg ,因为有重边, // 然后统计叶子节点个数,连边解决。
H - Prince and Princess
题意:就是让你求,保证最大的二分图匹配的,每个点的匹配情况。