1. 程式人生 > >HDU 3749 Financial Crisis (點雙連通+並查集)

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