HDU 2460 Network(tarjan边双联通)

HDU 2460 Network

题意

给一个连通无向图,在这个图的基础上给出一些连边的操作,问每次操作后还剩多少个桥?

思路

首先跑一次tarjan不用说,这里尤注意与求scc的tarjan不同,关于桥的判断,要在dfs
结束回溯的时候直接if(low[v]>dfn[u])判断。

然后对于每一个操作,判断要加的两个点在不在一个双连通分量,
如果在一个双连通分量里面,不用进行任何操作(这样加边不会减少桥);
如果不在一个双连通分量里面,就会形成新的环,这个时候把两点之间的路径上的桥的标记全部去掉即可。
(注意代码中“边权下放”的思想)。
所谓“边权下放”的思想,我认为,是把本来以边为下标存储的信息变成以点为下标的思想。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
const int maxn=100000+10,maxm=200000+10;
stack<int> s;
vector<int> G[maxn];
//struct edge{
//  int v,next;
//}a[maxm];
//int h[maxn],tot;
/*void add_e(int x,int y){
    a[tot].v=y;
    a[tot].next=h[x];
    h[x]=tot++;
}*/
int dfn[maxn],low[maxn],isBri[maxn],belong[maxn],father[maxn];
int dfs_clock,cnt_bri;
//int f[maxn][25],dep[maxn];
void dfs(int u,int fa){
    father[u]=fa;
    low[u]=dfn[u]=++dfs_clock;
    s.push(u);
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
//      if(i/2==fa/2&&fa!=-1)continue;
        if(v==fa)continue;
        if(!dfn[v]){
//          dfs(v,i);
            dfs(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u]){
                isBri[v]=1;//“边权下放” 
                cnt_bri++;
                int x;
                do{
                    x=s.top();s.pop();
                    belong[x]=cnt_bri;
                }while(x!=v);
            }
        }else{
            low[u]=min(low[u],dfn[v]);
        }
    }
}
/*void dfs2(int u,int fa){
    dep[u]=dep[fa]+1;
    f[u][0]=fa;
    for(int i=1;i<=20;i++)
        f[u][i]=f[f[u][i-1]][i-1];
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v==fa)continue;
        dfs2(v,u);
    }
}*/
/*int lca(int x,int y){
    if(dep[x]<dep[y])
        swap(x,y);
    int dc=dep[x]-dep[y];
    for(int i=0;i<=20;i++){
        if((1<<i)&dc)
            x=f[x][i];
    }
    if(x==y)return x;
    for(int i=20;i>=0;i--){
        if(f[x][i]!=f[y][i]){
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}*/
/*void mark(int u,int goal){
    while(u!=goal){
        if(isBri[u]){
            cnt_bri--;
            isBri[u]=0;
        }
        u=f[u][0];
    }
}*/
void func(int x,int y){
    while(x!=y){
        if(isBri[x]){
            isBri[x]=0;
            cnt_bri--;
        }
        if(isBri[y]){
            isBri[y]=0;
            cnt_bri--;
        }
        x=father[x];
        y=father[y];
    }
}
int main(){
//  freopen("JJJ.in","r",stdin);
    int n,m,kase=0;
    while(scanf("%d%d",&n,&m)==2&&(n+m)){
        kase++;
        printf("Case %d:\n",kase);
//      tot=0;
//      memset(h,-1,sizeof h);
        for(int i=1;i<=n;i++)
            G[i].clear();
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
//          add_e(x,y);
//          add_e(y,x);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        memset(dfn,0,sizeof dfn);
        memset(low,0,sizeof low);
        memset(isBri,0,sizeof isBri);
        memset(belong,0,sizeof belong);
        dfs_clock=cnt_bri=0;
//      dfs(1,-1);//tarjan
        dfs(1,0);//tarjan
//      dfs2(1,0);//lca
//      printf("%d\n",cnt_bri);
        int k;
        scanf("%d",&k);
        while(k--){
            int x,y;
            scanf("%d%d",&x,&y);
            if(belong[x]!=belong[y]){
//              int anc=lca(x,y);
//              mark(x,anc);
//              mark(y,anc);
                func(x,y);
            }
            printf("%d\n",cnt_bri);
        }
        printf("\n");
    }
    return 0;
}

后记

此题巨坑,由于N的范围太大,导致写lca的时候树上倍增会MLE,所以这里要写暴力的。
但是我感觉我的暴力lca好像有点错了,不过莫名其妙地也A了。
有空改一下。
posted @ 2016-11-04 10:52  yohanlong  阅读(183)  评论(0编辑  收藏  举报