zoukankan      html  css  js  c++  java
  • 边的双联通+缩点+LCA(HDU3686)

    Traffic Real Time Query System

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 1929    Accepted Submission(s): 380


    Problem Description
    City C is really a nightmare of all drivers for its traffic jams. To solve the traffic problem, the mayor plans to build a RTQS (Real Time Query System) to monitor all traffic situations. City C is made up of N crossings and M roads, and each road connects two crossings. All roads are bidirectional. One of the important tasks of RTQS is to answer some queries about route-choice problem. Specifically, the task is to find the crossings which a driver MUST pass when he is driving from one given road to another given road.
     
    Input
    There are multiple test cases.
    For each test case:
    The first line contains two integers N and M, representing the number of the crossings and roads.
    The next M lines describe the roads. In those M lines, the ith line (i starts from 1)contains two integers Xi and Yi, representing that roadi connects crossing Xi and Yi (Xi≠Yi).
    The following line contains a single integer Q, representing the number of RTQs.
    Then Q lines follows, each describing a RTQ by two integers S and T(S≠T) meaning that a driver is now driving on the roads and he wants to reach roadt . It will be always at least one way from roads to roadt.
    The input ends with a line of “0 0”.
    Please note that: 0<N<=10000, 0<M<=100000, 0<Q<=10000, 0<Xi,Yi<=N, 0<S,T<=M
     
    Output
    For each RTQ prints a line containing a single integer representing the number of crossings which the driver MUST pass.
     
    Sample Input
    5 6
    1 2
    1 3
    2 3
    3 4
    4 5
    3 5
    2
    2 3
    2 4
    0 0
     
    Sample Output
    0
    1
    题意:给出一个无向连通图,然后给出S,T分别代表起始路和终止路的编号,问之间必须要经过的点有多少个?
    分析:这样的点一定是割点,首先用tarjan算法找出割点(第一类点),然后求出点联通分量(边构成的块形成第二类点),缩点后形成一棵树,第一类点和第二类点是交叉相连的,所以用LCA找出最短路径后的距离/2就是结果;
    程序:
    #include"string.h"
    #include"stdio.h"
    #include"iostream"
    #include"queue"
    #include"stack"
    #define M 10009
    #define N 100009
    #include"stdlib.h"
    #include"math.h"
    #define inf 99999999
    using namespace std;
    struct node//构建原图
    {
        int u,v,next,vis;
    }edge[N*2];
    stack<int>q;
    int t,head[M],dfn[M],low[M],cut[M],use[N*2],index,num,belong[N*2];
    struct Tree//缩点后的图
    {
        int v;
        Tree(){}
        Tree(int vv):v(vv){}
    };
    vector<Tree>Edge[M+N];
    void init()
    {
        t=0;
        memset(head,-1,sizeof(head));
        memset(edge,0,sizeof(edge));
    }
    void add(int u,int v)//原图建边
    {
        edge[t].u=u;
        edge[t].v=v;
        edge[t].next=head[u];
        head[u]=t++;
    }
    void tarjan(int u,int fa)
    {
        dfn[u]=low[u]=++index;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(edge[i].vis)continue;
            edge[i].vis=edge[i^1].vis=1;
            q.push(i);
            if(!dfn[v])
            {
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
                if(low[v]>=dfn[u])//求割点
                {
                    num++;
                    cut[u]++;
                    int j;//求边联通块
                    do
                    {
                        j=q.top();
                        q.pop();
                        belong[j]=belong[j^1]=num;//形成边连通块
                    }while(j!=i);
                }
            }
            else
                low[u]=min(low[u],dfn[v]);
        }
        if(fa<0)
            cut[u]--;
    }
    void solve(int n)
    {
        index=num=0;
        memset(dfn,0,sizeof(dfn));
        memset(cut,0,sizeof(cut));
        for(int i=1;i<=n;i++)
            if(!dfn[i])
            tarjan(i,-1);
    }
    struct LCA
    {
        int u,v,w,next;
    }lca[N*3];
    int t1,head1[N*2],f[N*2],dis[N*2];
    void Init()
    {
        t1=0;
        memset(head1,-1,sizeof(head1));
    }
    void Addlca(int u,int v)
    {
        lca[t1].u=u;
        lca[t1].v=v;
        lca[t1].next=head1[u];
        head1[u]=t1++;
    }
    int finde(int x)
    {
        if(x!=f[x])
            f[x]=finde(f[x]);
        return f[x];
    }
    void make(int a,int b)
    {
        f[finde(a)]=finde(b);
    }
    void dfs(int u)//离线LCA算法
    {
        use[u]=1;
        for(int i=0;i<(int)Edge[u].size();i++)
        {
            int v=Edge[u][i].v;
            if(!use[v])
            {
                dis[v]=dis[u]+1;
                dfs(v);
                f[v]=u;
                make(u,v);
            }
        }
        for(int i=head1[u];i!=-1;i=lca[i].next)
        {
            int v=lca[i].v;
            if(use[v])
                lca[i].w=lca[i^1].w=f[finde(v)];
        }
    }
    void slove()
    {
        dis[1]=0;
        for(int i=0;i<=num;i++)
        f[i]=i;
        memset(use,0,sizeof(use));
        for(int i=1;i<=num;i++)
            if(!use[i])
            dfs(i);
        for(int i=0;i<t1;i+=2)
        {
            int u=lca[i].u;
            int v=lca[i].v;
            int mid=lca[i].w;
            printf("%d
    ",(dis[u]+dis[v]-2*dis[mid])/2);
        }
    }
    int main()
    {
        int n,m,i,u,v;
        while(scanf("%d%d",&n,&m),m||n)
        {
            init();
            for(i=0;i<m;i++)
            {
                scanf("%d%d",&u,&v);
                add(u,v);
                add(v,u);
            }
            solve(n);//求割点和边联通块
            memset(use,0,sizeof(use));
            for(u=1;u<=n;u++)
            {
                if(cut[u])
                {
                    ++num;//在边的联通块的序号之上继续给割点编号
                    for(i=head[u];i!=-1;i=edge[i].next)
                    {
                        if(!use[belong[i]])//某个割点和相邻某条边建边后即与边联通块连接,应该去重
                        {
                            Edge[num].push_back(belong[i]);
                            Edge[belong[i]].push_back(num);
                            use[belong[i]]=1;
                        }
                    }
                    for(i=head[u];i!=-1;i=edge[i].next)
                        use[belong[i]]=0;
                }
            }
            int Q;
            scanf("%d",&Q);
            Init();
            while(Q--)
            {
                scanf("%d%d",&u,&v);//输入的是原始边的排列序号
                Addlca(belong[u*2-1],belong[v*2-1]);//把边映射到边联通块中
                Addlca(belong[v*2-1],belong[u*2-1]);
            }
            slove();
            for(i=0;i<=num;i++)
                Edge[i].clear();
        }
    }
    View Code
     
  • 相关阅读:
    【转】SQL时间函数
    C#操作Word完全方法
    出水芙蓉,风华绝代记民国才女林徽因
    梅超风:我就是那个多年以前的女子
    厉胜男
    南海恶神
    挪窝
    吴若权——洛可可动画电影馆
    美的慢箭
    机械公敌(I, Robot) 场景设定
  • 原文地址:https://www.cnblogs.com/mypsq/p/4348134.html
Copyright © 2011-2022 走看看