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
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(); } }