边的双联通+缩点+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\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

 

 
posted @ 2014-10-11 12:32  一样菜  阅读(213)  评论(0编辑  收藏  举报