poj 3694 Network(割边+lca)

题目链接:http://poj.org/problem?id=3694

题意:一个无向图中本来有若干条桥,有Q个操作,每次加一条边(u,v),每次操作后输出桥的数目。

分析:通常的做法是:先求出该无向图的桥的数目count和边双连通分量,缩点,每次加边(u,v),判断若u,v属于同一个双连通分量,则桥的数目不变,否则,桥的数目必定会减少,这时桥减少的数目明显和最近公共祖先lca有关,用裸的lca就行了,每次u和v向父节点回退,如果该节点是桥的端点,则count--,直到u==v为止。

有个优化:其实不用缩点,只要在求出桥的时候标记一下,然后在用lca求减少桥的数目时,利用dfn[]就行了,因为u,v的最近公共祖先dfn[]一定相等。

AC代码如下:

  1 #include<cstdio>
  2 #include<cstring>
  3 const int N=1000000+5;
  4 const int M=2000000+5;
  5 struct EDGE{
  6     int v,next;
  7 }edge[M];
  8 int first[N],dfn[N],low[N],bri[N],pre[N];
  9 int g,cnt,count;
 10 void AddEdge(int u,int v)
 11 {
 12     edge[g].v=v;
 13     edge[g].next=first[u];
 14     first[u]=g++;
 15 }
 16 int min(int a,int b)
 17 {
 18     return a<b?a:b;
 19 }
 20 void Tarjan(int u,int fa)
 21 {
 22     int i,v;
 23     low[u]=dfn[u]=++cnt;
 24     for(i=first[u];i!=-1;i=edge[i].next)
 25     {
 26         v=edge[i].v;
 27         if(i==(fa^1))
 28             continue;
 29         if(!dfn[v])
 30         {
 31             pre[v]=u;
 32             Tarjan(v,i);
 33             low[u]=min(low[u],low[v]);
 34             if(low[v]>dfn[u])
 35             {
 36                 count++;
 37                 bri[v]=1;
 38             }
 39         }
 40         else
 41             low[u]=min(low[u],dfn[v]);
 42     }
 43 }
 44 void lca(int u,int v)
 45 {
 46     while(dfn[u]>dfn[v])
 47     {
 48         if(bri[u])
 49         {
 50             count--;
 51             bri[u]=0;
 52         }
 53         u=pre[u];
 54     }
 55     while(dfn[v]>dfn[u])
 56     {
 57         if(bri[v])
 58         {
 59             count--;
 60             bri[v]=0;
 61         }
 62         v=pre[v];
 63     }
 64     while(u!=v)
 65     {
 66         if(bri[u])
 67         {
 68             count--;
 69             bri[u]=0;
 70         }
 71         if(bri[v])
 72         {
 73             count--;
 74             bri[v]=0;
 75         }
 76         u=pre[u];
 77         v=pre[v];
 78     }
 79 }
 80 int main()
 81 {
 82     int n,m,i,u,v,q;
 83     int cas=1;
 84     while(scanf("%d%d",&n,&m)!=EOF)
 85     {
 86         if(n==0&&m==0)
 87             break;
 88         g=cnt=count=0;
 89         memset(first,-1,sizeof(first));
 90         memset(dfn,0,sizeof(dfn));
 91         memset(bri,0,sizeof(bri));
 92         for(i=0;i<m;i++)
 93         {
 94             scanf("%d%d",&u,&v);
 95             AddEdge(u,v);
 96             AddEdge(v,u);
 97         }
 98         for(i=1;i<=n;i++)
 99             if(!dfn[i])
100                 Tarjan(i,-1);
101         scanf("%d",&q);
102         printf("Case %d:
",cas++);
103         for(i=0;i<q;i++)
104         {
105             scanf("%d%d",&u,&v);
106             lca(u,v);
107             printf("%d
",count);
108         }
109         printf("
");
110     }
111     return 0;
112 }
View Code
原文地址:https://www.cnblogs.com/frog112111/p/3382108.html