首页 > 其他分享 >CF723F st-Spanning Tree - 贪心 - 图论 -

CF723F st-Spanning Tree - 贪心 - 图论 -

时间:2022-12-08 21:34:32浏览次数:74  
标签:int Tree st init maxn CF723F dt include

题目链接:https://codeforces.com/problemset/problem/723/F

题解:
首先先删去 s 和 t,原图一定是若干个连通块,先把这些块的生成森林求出来,之后将连通块缩点
然后考虑如何与 s/t 连边?首先对于每个缩完的点(下称点)来说,一定是至少和 s/t 其中一个点连边(原图连通)。如果只与 s/t 中的 1 个相连,那就直接连就行了,否则,先贪心的连 ds/dt 剩余量多的对应的 s/t,注意这样操作完之后变成了2个连通块,而且 s/t 分属于两个块中。最后就连那一条边即可,要么是s-t直接连(如果有边),要么就是某个点既连 s 又连 t
细节很多,代码不好写

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cassert>
#include <cstring>
#include <set>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 4e5 + 5;

int n,m;
vector<int>g[maxn];
set<int>h[maxn], poi[maxn];
int x[maxn], y[maxn];
int s,t,ds,dt,vis[maxn],cnt2;
vector<pii>init_st;
int con[maxn][2], v[maxn];
// con[][0/1] 缩点之后与 s/t 连通 v[] 缩点之后第一轮是和 s/t 连边(防止连重了) 

void dfs(int x,int id){
	vis[x] = id;
	for(int u : g[x])if(!vis[u]){
		init_st.push_back(mpr(x,u));	// 每个连通块的生成森林一定在答案中 
		dfs(u, id);
	}
}

signed main(){
//	freopen("CF723F.in","r",stdin);
//	freopen("CF723F.out","w",stdout);
	int fg = 0;
	scanf("%d%d",&n,&m); 
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x[i],&y[i]);
	}
	scanf("%d%d%d%d",&s,&t,&ds,&dt);
	if(s > t)swap(s, t), swap(ds, dt);
	for(int i=1;i<=m;i++){
		if(s == x[i] || s == y[i] || t == x[i] || t == y[i]){
		}else g[x[i]].push_back(y[i]), g[y[i]].push_back(x[i]);
		// 建图 删去 和 s/t 相连的边 
	}
	int cnt = 0;
	for(int i=1;i<=n;i++)
		if(i!=s && i!=t && !vis[i]){
			dfs(i, ++cnt);
		}
		
	// x -> vis[x] 缩点 
	for(int i=1;i<=m;i++){
		if((x[i]==s&&y[i]==t) || (x[i]==t&&y[i]==s)){fg = 1;continue;}
		if(s == x[i] || s == y[i] || t == x[i] || t == y[i]){
			int fx = x[i], fy = y[i];
			if(s == x[i] || t == x[i])swap(fx, fy);
			// fx -> s/t
			poi[vis[fx]].insert(fy);
			con[vis[fx]][fy == s ? 0 : 1] = fx;
		}
	}
	// 缩点之后每个点先和 s/t 连一条边,连完之后图一定是 2 个连通块 
	// 先连只与s/t一个相连的,再连两个都相连的(只需要连一条) 
	for(int i=1;i<=cnt;i++){
		if(poi[i].size() == 1){
			int to = *poi[i].begin();
			if(to == s){
				-- ds;
				init_st.push_back(mpr(con[i][0], s));
			}
			else{
				--dt; 
				init_st.push_back(mpr(con[i][1], t));
			}
		}
	}
	
	int gg = 0;
	for(int i=1;i<=cnt;i++)
		if(poi[i].size() == 2){
			if(ds > 0){
				v[i] = 1;
				-- ds;
				init_st.push_back(mpr(con[i][0], s));
			}else if(dt > 0){
				v[i] = 2;
				-- dt;
				init_st.push_back(mpr(con[i][1], t));
			}else gg = 1;
		}
		
		// 最后再连1条,使得图连通(某个点与s/t均连 或者 s-t 直接连) 
	int ok = 0;
	if(ds+dt > 0){
		if(ds > 0 && dt > 0 && fg)init_st.push_back(mpr(s, t)), ok = 1;
		else
			for(int i=1;i<=cnt;i++){
				if(poi[i].size() == 2){
					if(ds > 0 && v[i] != 1){
						ok=1;
						-- ds;
						init_st.push_back(mpr(con[i][0], s));
					}else if(dt > 0 && v[i] != 2){
						ok=1;
						-- dt;
						init_st.push_back(mpr(con[i][1], t));
					}
					break;
				}
			}
	}

	if(!ok || gg)puts("No");
	else{
		puts("Yes");
		for(pii u : init_st)printf("%d %d\n",u.first,u.second);
	}

	return 0;
}

标签:int,Tree,st,init,maxn,CF723F,dt,include
From: https://www.cnblogs.com/SkyRainWind/p/16967394.html

相关文章