zoukankan      html  css  js  c++  java
  • poj 3694(割边+lca)

    题意:给你一个无向图,可能有重边,有q次询问,问你每次我添加一条边,添加后这个图还有多少个桥

    解题思路:首先先把所有没有割边的点对缩成一个联通块,无向图一般并查集判环,然后就得到一个割边树,给你一条新边,找到这条边两个端点的所属的联通块,如果这两个端点属于用一个联通块,那么没有作用,属于不同的联通块的时候,找到他们的lca,路径上有多少边,就减去多少割边,然后成为一个新的联通块,具体见代码

    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstring>
    using namespace std;
    const int maxn=100500;
    struct Edge
    {
        int next;int to;int id;
    }edge[maxn<<2];
    int head[maxn],cnt,dfn[maxn],low[maxn];
    int fa[maxn],pre[maxn],ans,step,n,m;
    void init()
    {
        memset(head,-1,sizeof(head));cnt=ans=step=0;
        for(int i=1;i<=n;i++)
            fa[i]=i;
        memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));
    
    }
    int findf(int u)
    {
        if(fa[u]==u)
            return u;
        else
        {
            fa[u]=findf(fa[u]);
            return fa[u];
        }
    }
    bool join(int x,int y)
    {
        int t1=findf(x);
        int t2=findf(y);
        if(t1==t2)
            return false;
        else
        {
            fa[t2]=t1;
            return true;
        }
    }
    void add(int u,int v,int id)//id是用来判是否有重边的
    {
        edge[cnt].next=head[u];edge[cnt].to=v;edge[cnt].id=id;head[u]=cnt++;
        edge[cnt].next=head[v];edge[cnt].to=u;edge[cnt].id=id;head[v]=cnt++;
    }
    void tarjan(int u,int fa)
    {
        dfn[u]=low[u]=++step;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            int id=edge[i].id;
            if(fa==id)
                continue;
            if(!dfn[v])
            {
                pre[v]=u;//记录他的父亲结点
                tarjan(v,id);
                low[u]=min(low[v],low[u]);
                if(dfn[u]<low[v])
                {
                    ans++;
                }
                else
                {
                    join(u,v);//不是割边就合并
                }
    
            }
            else
            {
                low[u]=min(low[u],dfn[v]);
            }
        }
    }
    void lca(int u,int v)
    {
        if(findf(u)==findf(v))
            return;
        if(dfn[v]<dfn[u])//根据之前pre数组,从小的位置开始
            swap(u,v);
        while(dfn[v]>dfn[u])
        {
            if(join(pre[v],v))//路径上的割边全部合并
            {
                ans--;
            }
            v=pre[v];
        }
        while(u!=v)//如果是1的另一个子树中,因为上一段最多判到1,还需要判另一半
        {
            if(join(pre[u],u))
                ans--;
            u=pre[u];
        }
    }
    int main()
    {
        int x,y;
        int k,cot;
        cot=0;
        while(scanf("%d%d",&n,&m)&&n&&m)
        {
            cot++;
            init();
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d",&x,&y);add(x,y,i);
            }
            tarjan(1,0);
            pre[1]=1;
            printf("Case %d:
    ",cot);
            scanf("%d",&k);
            while(k--)
            {
                scanf("%d%d",&x,&y);
                lca(x,y);
                printf("%d
    ",ans);
            }
        }
    }

  • 相关阅读:
    解决Enterprise Library January 2006不能加密配置文件的方法
    ASP.NET Ajax 和ASP.NET 2.0 的登陆控件相冲突的问题的讨论
    十二时辰与时间对照表,十二经络时辰表
    对表中数据逐行累加
    SQL脚本 CASE...WHEN...THEN...ELSE...END 的应用
    [转]看刚毕业MM如何在北京买房
    让你的GUI程序随WINDOWS服务一起启动
    启动Oracle,SQL服务,IIS脚本
    无论买新房还是二手房 教你六招可放心收房
    经典开源项目简介及源码下载
  • 原文地址:https://www.cnblogs.com/huangdao/p/10618059.html
Copyright © 2011-2022 走看看