【生成树趣题】CF723F st-Spanning Tree
题意:
给定一个n个点m条边的无向联通图,没有重边和自环。给定s和t,求一棵生成树,使得s,t的度数不超过ds,dt。若有解,输出“Yes”和方案(多组方案输出任意一组),若无解,输出“No”。
数据范围:
2 ≤ n ≤ 200 000
分析:
首先,可以把边分成两类:一类是端点含s或t的,另一类是和s,t没有任何关系的。第二类边可以随便乱连,而第一类边不可以。
所以,就先把第二类边乱连,随便连,反正没有边权连就是了,用一个并查集,相当于搞一个生成树森林出来,这样我们就有了很多很多块集合(相当于被缩成一个点,要注意细节)和s,t两个点。
集合中只与s,t的其中一个点有边的,只能连那个边,否则就不联通了。一边连一边判断s,t的度数,如果超过了就判定无解。
集合中和s,t都有边相连的,选一个和s,t都连一条边,这样可以使s,t联通的同时尽量最小化度数。(s,t直接相连,度数各增加1,某一个集合再和s,t中的一个相连,s,t中的一个度数会增加1)
剩下的和s,t都有边相连的集合,随便连s,t都可以。(如果一个点的度数被连完了,就连另外那个)
最后,如果没有和s,t都有边相连的集合,那么此时s,t还没有被连起来,那么还要在s,t之间直接连一条边。
注意还要判断它是不是一棵生成树的形态(边数为n-1)
1 #include<cstdio> 2 #include<iostream> 3 #include<iomanip> 4 #include<algorithm> 5 #include<vector> 6 #include<queue> 7 using namespace std; 8 #define ll long long 9 #define N 200005 10 int n,m,s,t,ds,dt; 11 vector<int>G[N]; 12 vector<pair<int,int> >ans; 13 int f[N];//并查集 14 int ls[N],lt[N];//编号为i的集合通过ls[i]/lt[i]和s/t相连 15 inline int rd() 16 { 17 int f=1,x=0;char c=getchar(); 18 while(c<'0'||'9'<c){if(c=='-')f=-f;c=getchar();} 19 while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); 20 return f*x; 21 } 22 int Find(int x) 23 { 24 if(f[x]==x) return x; 25 return f[x]=Find(f[x]); 26 } 27 bool Union(int u,int v) 28 { 29 int x=Find(u),y=Find(v); 30 if(x==y) return false; 31 if(x<y) f[y]=x; 32 else f[x]=y; 33 return true; 34 } 35 void Init() 36 { 37 for(int i=1;i<=n;i++) 38 f[i]=i; 39 } 40 int main() 41 { 42 n=rd(),m=rd(); 43 for(int i=1;i<=m;i++) 44 { 45 int u=rd(),v=rd(); 46 G[u].push_back(v); 47 G[v].push_back(u); 48 } 49 s=rd(),t=rd(),ds=rd(),dt=rd(); 50 Init(); 51 //乱连不含s,t的边 52 for(int u=1;u<=n;u++) 53 { 54 if(u==s||u==t) continue; 55 for(int i=0;i<G[u].size();i++) 56 { 57 int v=G[u][i]; 58 if(v==s||v==t) continue; 59 if(Union(u,v)) ans.push_back(make_pair(u,v)); 60 } 61 } 62 //处理集合的哪些边和s,t相连的情况 63 for(int i=0;i<G[s].size();i++) 64 if(G[s][i]!=t) 65 { 66 int x=Find(G[s][i]); 67 ls[x]=G[s][i]; 68 } 69 for(int i=0;i<G[t].size();i++) 70 if(G[t][i]!=s) 71 { 72 int x=Find(G[t][i]); 73 lt[x]=G[t][i]; 74 } 75 //处理只和s,t中的一个相连的集合 76 for(int i=1;i<=n;i++) 77 { 78 if(ls[i]&&!lt[i]) 79 { 80 ds--; 81 Union(s,i); 82 ans.push_back(make_pair(s,ls[i])); 83 } 84 else if(!ls[i]&<[i]) 85 { 86 dt--; 87 Union(t,i); 88 ans.push_back(make_pair(t,lt[i])); 89 } 90 if(ds<0||dt<0) 91 { 92 puts("No"); 93 return 0; 94 } 95 } 96 //处理和s,t都相连的集合 第一个会和s,t都连 s,t连通之后就只会连一个 97 for(int i=1;i<=n;i++) 98 if(ls[i]&<[i]) 99 { 100 if(ds&&Union(s,i)) 101 {/*短路运算符 Union不能写在前面 或者也可以用find()判断 Union写在里面*/ 102 ds--; 103 ans.push_back(make_pair(s,ls[i])); 104 } 105 if(dt&&Union(t,i)) 106 { 107 dt--; 108 ans.push_back(make_pair(t,lt[i])); 109 } 110 if(Find(s)!=Find(i)||Find(t)!=Find(i)) //说明前面没有办法连 度数都用完了 111 { 112 puts("No"); 113 return 0; 114 } 115 } 116 if(Find(s)!=Find(t))//s,t还没有连通 117 { 118 bool f=0; 119 for(int i=0;i<G[s].size();i++) 120 if(G[s][i]==t) 121 { 122 f=1; 123 break; 124 } 125 if(ds&&dt&&f) 126 ans.push_back(make_pair(s,t)); 127 if(!f) 128 { 129 puts("No"); 130 return 0; 131 } 132 } 133 if(ans.size()!=n-1) 134 {//满足是一棵生成树 135 puts("No"); 136 return 0; 137 } 138 puts("Yes"); 139 for(int i=0;i<ans.size();i++) 140 printf("%d %d\n",ans[i].first,ans[i].second); 141 return 0; 142 }
转载请注明出处,有疑问欢迎探讨
博主邮箱 2775182058@qq.com