POJ3694 Network(边双连通分量+缩点+LCA)
题目大概是给一张图,动态加边动态求割边数。
本想着求出边双连通分量后缩点,然后构成的树用树链剖分+线段树去维护路径上的边数和。。好像好难写。。
看了别人的解法,这题有更简单的算法:
在任意两点添边,那么两点路径上的边就不是割边了,于是从两点往上走到其LCA,一边缩点一边统计消失的割边数。
这样的时间复杂度是保证的,因为最多就把所有点缩完而最多走的边数差不多就原图的边数。
具体实现,用Tarjan求出边双连通分量后缩点;缩点用并查集,要注意合并次序深度小的作深度大的点的根;最后就是对每个询问的两个点向上走到其LCA一边走一边更新。
另外,不必去维护缩点后的树,直接维护Tarjan算法构造的深度优先生成树就行了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define MAXN 111111 6 #define MAXM 444444 7 struct Edge{ 8 int v,next; 9 bool flag; 10 }edge[MAXM]; 11 int NE,head[MAXN]; 12 void addEdge(int u,int v){ 13 edge[NE].v=v; edge[NE].next=head[u]; edge[NE].flag=0; 14 head[u]=NE++; 15 } 16 17 int par[MAXN]; 18 int Find(int a){ 19 while(a!=par[a]){ 20 par[a]=par[par[a]]; 21 a=par[a]; 22 } 23 return a; 24 } 25 void Union(int a,int b){ 26 int pa=Find(a),pb=Find(b); 27 if(pa==pb) return; 28 par[pb]=pa; 29 } 30 31 int fa[MAXN],dep[MAXN],cut; 32 int dn,dfn[MAXN],low[MAXN]; 33 void dfs(int u){ 34 dfn[u]=low[u]=++dn; 35 for(int i=head[u]; i!=-1; i=edge[i].next){ 36 if(edge[i].flag) continue; 37 int v=edge[i].v; 38 if(dfn[v]){ 39 low[u]=min(low[u],dfn[v]); 40 continue; 41 } 42 fa[v]=u; dep[v]=dep[u]+1; 43 edge[i].flag=edge[i^1].flag=1; 44 dfs(v); 45 low[u]=min(low[u],low[v]); 46 if(low[v]>dfn[u]) ++cut; 47 else Union(u,v); 48 } 49 } 50 51 int lca(int u,int v){ 52 int cnt=0; 53 u=Find(u); v=Find(v); 54 while(u!=v){ 55 if(dep[u]>dep[v]){ 56 Union(fa[u],u); ++cnt; 57 u=Find(fa[u]); 58 }else if(dep[v]>dep[u]){ 59 Union(fa[v],v); ++cnt; 60 v=Find(fa[v]); 61 }else{ 62 Union(fa[u],u); Union(fa[v],v); cnt+=2; 63 u=Find(fa[u]); v=Find(fa[v]); 64 } 65 } 66 return cnt; 67 } 68 int main(){ 69 int n,m,a,b,t=0; 70 while(~scanf("%d%d",&n,&m) && (n||m)){ 71 NE=0; 72 memset(head,-1,sizeof(head)); 73 while(m--){ 74 scanf("%d%d",&a,&b); 75 addEdge(a,b); addEdge(b,a); 76 } 77 78 for(int i=1; i<=n; ++i) par[i]=i; 79 cut=dn=0; 80 memset(dfn,0,sizeof(dfn)); 81 dfs(1); 82 83 printf("Case %d:\n",++t); 84 scanf("%d",&m); 85 while(m--){ 86 scanf("%d%d",&a,&b); 87 cut-=lca(a,b); 88 printf("%d\n",cut); 89 } 90 putchar('\n'); 91 } 92 return 0; 93 }