【Codeforces Round 375 (Div 2) F】【构造 贪心】st-Spanning Tree 树的特殊最小生成树使得S度不超SD,T度不超TD

84 篇文章 5 订阅
24 篇文章 0 订阅
F. st-Spanning Tree
time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given an undirected connected graph consisting of n vertices and m edges. There are no loops and no multiple edges in the graph.

You are also given two distinct vertices s and t, and two values ds and dt. Your task is to build any spanning tree of the given graph (note that the graph is not weighted), such that the degree of the vertex s doesn't exceed ds, and the degree of the vertex t doesn't exceed dt, or determine, that there is no such spanning tree.

The spanning tree of the graph G is a subgraph which is a tree and contains all vertices of the graph G. In other words, it is a connected graph which contains n - 1 edges and can be obtained by removing some of the edges from G.

The degree of a vertex is the number of edges incident to this vertex.

Input

The first line of the input contains two integers n and m (2 ≤ n ≤ 200 0001 ≤ m ≤ min(400 000, n·(n - 1) / 2)) — the number of vertices and the number of edges in the graph.

The next m lines contain the descriptions of the graph's edges. Each of the lines contains two integers u and v (1 ≤ u, v ≤ nu ≠ v) — the ends of the corresponding edge. It is guaranteed that the graph contains no loops and no multiple edges and that it is connected.

The last line contains four integers stdsdt (1 ≤ s, t ≤ ns ≠ t1 ≤ ds, dt ≤ n - 1).

Output

If the answer doesn't exist print "No" (without quotes) in the only line of the output.

Otherwise, in the first line print "Yes" (without quotes). In the each of the next (n - 1) lines print two integers — the description of the edges of the spanning tree. Each of the edges of the spanning tree must be printed exactly once.

You can output edges in any order. You can output the ends of each edge in any order.

If there are several solutions, print any of them.

Examples
input
3 3
1 2
2 3
3 1
1 2 1 1
output
Yes
3 2
1 3
input
7 8
7 4
1 3
5 4
5 7
3 2
2 4
6 1
1 2
6 4 1 4
output
Yes
1 3
5 7
3 2
7 4
2 4
6 1

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 2e5 + 10, M = 4e5+10, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int n, m;
pair<int, int>a[M];
int S, T, SD, TD;
int f[N];
bool s[N], t[N];
int find(int x)
{
	return f[x] == x ? x : f[x] = find(f[x]);
}
int con[N];
pair<int, int>ans[M];
bool solve()
{
	//第一步,做基础联通并判定
	int g = 0;
	int ST = 0;
	for (int i = 1; i <= m; ++i)
	{
		int x = a[i].first;
		int y = a[i].second;
		if (y == S)swap(x, y);
		if (x == S && y == T)++ST;
		else if (x == S)s[find(y)] = 1;
		else
		{
			if (y == T)swap(x, y);
			if (x == T)t[find(y)] = 1;
			else
			{
				x = find(x);
				y = find(y);
				if (x != y)
				{
					f[y] = x;
					s[x] |= s[y];
					t[x] |= t[y];
					ans[++g] = a[i];
				}
			}
		}
	}
	//第二步,做合法性判定
	int both = 0;
	int SPE = 0;
	MS(con, 0);
	--SD;
	--TD;
	for (int i = 1; i <= n; ++i)if (i != S && i != T && i == find(i))
	{
		if (s[i] && t[i])
		{
			if (SPE == 0)SPE = i;
			else con[i] = -1, ++both;
		}
		else if (s[i])
		{
			if (--SD < 0)return 0;
			con[i] = S;
		}
		else if (t[i])
		{
			if (--TD < 0)return 0;
			con[i] = T;
		}
		else return 0;
	}
	int can = SD + TD;
	if (can < both)return 0;
	//第三步,贪心决策
	if (SPE)
	{
		int sta = 3;
		for (int i = 1; i <= m; ++i)
		{
			int x = a[i].first;
			int y = a[i].second;
			int fx = find(x);
			int fy = find(y);
			if (fy == SPE)swap(fx, fy), swap(x, y);
			if (fx == SPE)
			{
				if ((sta & 1) && y == S)
				{
					sta ^= 1;
					ans[++g] = a[i];
				}
				else if ((sta & 2) && y == T)
				{
					sta ^= 2;
					ans[++g] = a[i];
				}
			}
		}
	}
	else ans[++g] = { S,T };
	for (int i = 1; i <= m; ++i)
	{
		int x = a[i].first;
		int y = a[i].second;
		if (y == S)swap(x, y);
		if (x == S && y == T);
		else if (x == S)
		{
			int fy = find(y);
			if (con[fy] == S)//要和S连
			{
				ans[++g] = a[i];
				con[fy] = 0;
			}
			else if (con[fy] == -1 && SD)//可以和S连
			{
				con[fy] = 0;
				ans[++g] = a[i];
				--SD;
			}
		}
		else
		{
			if (y == T)swap(x, y);
			if (x == T)
			{
				int fy = find(y);
				if (con[fy] == T)//要和T连
				{
					ans[++g] = a[i];
					con[fy] = 0;
				}
				else if (con[fy] == -1 && TD)//可以和T连
				{
					con[fy] = 0;
					ans[++g] = a[i];
					--TD;
				}
			}
		}
	}
	puts("Yes");
	for (int i = 1; i <= g; ++i)printf("%d %d\n", ans[i].first, ans[i].second);
	return 1;
}
int main()
{
	while (~scanf("%d%d", &n, &m))
	{
		for (int i = 1; i <= m; ++i)scanf("%d%d", &a[i].first, &a[i].second);
		scanf("%d%d%d%d", &S, &T, &SD, &TD);
		for (int i = 1; i <= n; ++i)
		{
			s[i] = 0;
			t[i] = 0;
			f[i] = i;
		}
		if (!solve())puts("No");
	}
	return 0;
}
/*
【trick&&吐槽】
自己做题就是太不慌不忙了,
导致最后差一点点时间没写完,
把一个错误的代码交过去竟然就过了初测。
然而最后还是wa on test50

还有,变量定义一定要结构统一化,不然可能会不小心写错=w=

【题意】
给你一个联通无向图
让你构造一个子图,使得——
1,该子图是一棵树
2,S点的度数不超过SD
3,T点的度数不超过TD

【类型】
构造

【分析】
一,我们把点的两端都不是S或T的边取出来,用并查集连起来。
二,这个过程中我们同时维护——
	哪些集合可以和S连边
	哪些集合可以和T连边
	哪些集合可以和S、T都连边
	S与T之间是否有边
三,判定合法性——
	重新扫一遍所有集合
	如果该集合只能和S连边,就与S连,度数超额GG,否则标记该集合与S连
	如果该集合只能和T连边,就与T链,度数超额GG,否则标记该集合与T连
	如果该集合能和S与T都连边,则标记该集合可以与S和T联通,记做both
	这个时候,SD和TD都会有一个剩余度数,之和记做can
	如果剩余联通块个数(就是还没有和S或T任意一个连接的)超过了剩余度数(即both>can),也GG

	判定还未结束——
	如果存在both,即存在一个集合可以与S和T都连边,那我们就在这个集合的身上实现其余S和T的连边
		这样使得S与T连边了,我们多消耗的额外度数为1
	否则,不存在一个集合与S和T之间连边,那我们就要在S和T之间通过ST边直接连接。
		这样时而S与T连边了,我们多消耗的额外度数为2
	显然要最后万不得已才考虑ST边

	因为S与T的联通必然要消耗两者的度各1点。
	所以我们可以一开始就对两者的度做-1操作
	存在both找到一个可以与S和T都联通的联通块做联通操作
	不存在both则连ST边

	对于其他可以和S与T都联通的联通块,我们在保证正确的情况下连边即可。
		
【时间复杂度&&优化】
O(m)

*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值