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 }

 

posted @ 2016-01-23 21:08  WABoss  阅读(248)  评论(0编辑  收藏  举报