<题目连接>
题目大意:
给你一个(保证输入无重边,无自环)无向图,然后有下面Q条询问,每条询问为:问你u点与v点之间有几条(除了首尾两点外,其他点不重复)的路径.如果有0条或1条输出0或1,如果有2条以上,输出”two or more”.
解题分析:
我们可以用并查集判断两点之间是否有路径相连通,如果两点不连通,则直接输出0即可。至于判断两点之间有几条不重复的路径相连,则是通过这两点是否属于同一点双连通分量来判断。不过需要注意的是,我们应该排除只有两个点的点双连通分量这一特殊情况。所以综上,只要待查询的两点属于点数大于2的点双连通分量,则直接输出"two or more"(在已经判断这两点连通的情况下),否则输出"one"。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<stack> 6 using namespace std; 7 8 #define pb push_back 9 #define srep(i,s,t) for(int i=s;i<t;i++) 10 #define clr(a,b) memset(a,b,sizeof(a)) 11 const int N = 5e3+10; 12 int n,m,q; 13 int tot,bcc_ord; 14 int dfn[N],low[N],bcc_now[N],fa[N]; 15 vector<int> G[N], bcc[N],belong[N];//belong[i]表示第i个节点属于的所有点双连通分量编号 16 struct Edge{ 17 int u,v; 18 Edge(int u=0,int v=0):u(u),v(v){} 19 }; 20 void init(){ 21 bcc_ord=tot=0; 22 clr(dfn,0);clr(bcc_now,0);clr(fa,-1); 23 srep(i,0,n) 24 G[i].clear(),belong[i].clear(); 25 } 26 stack<Edge>stk; 27 void Tarjan(int u,int fa){ 28 low[u]=dfn[u]=++tot; 29 srep(i,0,G[u].size()){ 30 int v=G[u][i]; 31 if(v==fa) continue; 32 Edge e=Edge(u,v); 33 if(!dfn[v]){ 34 stk.push(e); 35 Tarjan(v,u); 36 low[u]=min(low[v],low[u]); 37 if(low[v]>=dfn[u]){ //割点 38 bcc_ord++;bcc[bcc_ord].clear(); 39 while(true){ //将栈中所有属于当前点双连通分量的点全部标记,注意,栈内存的是边 40 Edge x=stk.top(); stk.pop(); 41 int st = x.u,ed = x.v; 42 if(bcc_now[st]!=bcc_ord){ 43 bcc[bcc_ord].pb(st); //将u加入当前点双连通分量的节点集 44 bcc_now[st]=bcc_ord; //标记u点当前所述的点双连通分量编号,防止在遍历同一点双连通分量的过程中重复将点标记 45 belong[st].pb(bcc_ord); //一个点可能属于多个点双连通分量,belong[u]存储的是u点所属的多个点双连通分量 46 } 47 if(bcc_now[ed]!=bcc_ord){ 48 bcc[bcc_ord].pb(ed); 49 bcc_now[ed]=bcc_ord; 50 belong[ed].pb(bcc_ord); 51 } 52 if(st == u && ed == v) break; 53 } 54 } 55 } 56 else if(dfn[v]<dfn[u]){ 57 stk.push(e); 58 low[u]=min(low[u],dfn[v]); 59 } 60 } 61 } 62 int find(int i){ 63 if(fa[i]==-1) return i; 64 return fa[i]=find(fa[i]); 65 } 66 int main(){ 67 int ncase=0; 68 while(~scanf("%d%d%d",&n,&m,&q)&&n){ 69 init(); 70 while(m--){ 71 int u,v; 72 scanf("%d%d",&u,&v); 73 G[u].pb(v),G[v].pb(u); //加双向边构图 74 75 u=find(u), v=find(v); //并查集加边 76 if(u!=v) fa[u]=v; 77 } 78 srep(i,0,n) if(!dfn[i]) { 79 Tarjan(i,-1); 80 } 81 printf("Case %d:\n",++ncase); 82 while(q--){ 83 int u,v;scanf("%d%d",&u,&v); 84 if(find(u)!=find(v)) printf("zero\n"); //如果这两点不连通,直接输出0 85 else{ 86 bool flag=false; 87 for(int i=0;i<belong[u].size()&&!flag;i++) //遍历u和v所属的点双连通分量点集,判断是否有点双连通分量同时包含u和v 88 for(int j=0;j<belong[v].size()&&!flag;j++){ 89 if(belong[u][i]==belong[v][j]){ //如果存在这样的点双连通分量 90 int ord = belong[u][i]; //ord代表该点双连通分量的编号 91 if(bcc[ord].size()>2) //要如果该点双连通分量中点数>2,才能说明u和v之间一定有2条即两条以上的道路 92 puts("two or more"),flag=true; 93 } 94 } 95 if(!flag)puts("one"); //如果不存在点数>2的点双连通分量同时包含u和v,则说明u和v之间只有一条边相连 96 } 97 } 98 } 99 }
2018-12-02