HDU 3749 Financial Crisis (点双连通+并查集)

<题目连接>

题目大意:

  给你一个(保证输入无重边,无自环)无向图,然后有下面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

猜你喜欢

转载自www.cnblogs.com/00isok/p/10054027.html