边的双联通+缩点+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 i th line (i starts from 1)contains two integers X i and Y i, representing that road i connects crossing X i and Y i (X i≠Y i).
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<X i,Y i<=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\n",(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

 

 

你可能感兴趣的:(HDU)