【生成树趣题】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]&&lt[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]&&lt[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 }
Code

猜你喜欢

转载自www.cnblogs.com/lyttt/p/11772931.html