2024“钉耙编程”中国大学生算法设计超级联赛(6)1001 造花(简单版)

Problem Description

给定一棵 n 个点的树,请选择并删除这棵树上的一个点和连向这个点的所有边,使得整个图只剩下恰好两个连通块,且每个连通块都构成菊花图,请问这是否可以做到。

一个 n 个点的连通图是菊花图,当且仅当它是一棵树,且至少有一个点与其它 n−1 个点之间都有边直接相连。特别地,一个点的树也是菊花图。

Input

第一行一个整数 T(1≤T≤10^{5}),表示测试数据组数。

每组数据第一行一个整数 n(3≤n≤2×10^{5}),表示树的节点个数。

接下来 n−1 行描述了一棵树,每行两个整数 u 和 v(1≤u,v≤n),表示树上的一条边。

数据保证 ∑n≤2×10^{6}

Output

对于每组数据输出一行,如果可以通过删点操作使得整个图变成两个菊花图,输出 Yes,否则输出 No。

Sample Input

3
3
1 2
2 3
4
1 2
1 3
1 4
7
1 2
1 3
2 4
2 5
3 6
3 7

Sample Output

Yes

No

Yes

Hint

由于本题读入量较大,推荐使用 freadfread 函数进行数据读入。

思路:要能把一个树分成两个菊花图,要删去的点的度数必定为 2。如果它能被分成两个菊花图,度数不为 1 的点不会很多,仔细想想,它的度数为 2 的点最多为5个,所以每张图只需要检查五个不同的度数为 2 的点就好了。

检查整个图所有极大连通子图是不是菊花图,可以考虑使用并查集,对每个连通块记录点的个数,边的 个数和度数为 1 的点的个数,对于一个连通块,如果边的个数比点的个数少一个,且度数为 1 的点的个数和连通块的点的个数差值不大于 1 ,就说明这个子图是菊花图。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N=2e5+5;
int fa[N],u[N],v[N],in[N],sum[N],du[N];
int find(int x){
	return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
}
void merge(int x,int y){
	int tx=find(x),ty=find(y);
	fa[tx]=ty;
	du[x]++,du[y]++;//两个点的入读加 1
	sum[ty]+=sum[tx];//将sum[tx]这一集合点的个数给sum[ty]
}
signed main()
{
	IOS
	int t;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		fill(in+1,in+n+1,0);//初始化 
		for(int i=1;i<n;i++){
			cin >> u[i] >> v[i];
			in[u[i]]++,in[v[i]]++;//存度数 
		}
		int cnt=0,f=0;//cnt是度数为 2 的个数 
		for(int i=1;i<=n;i++){//断 i 这个点,判断能不形成两个菊花图 
			if(cnt>5) break;//分成两个菊花图最多有五个度数为 2 的点 
			if(in[i]!=2) continue;//只要度数为 2 的点 
			cnt++,f=1;
			fill(sum+1,sum+n+1,1);//初始化为 1 
			fill(du+1,du+n+1,0);//初始化为 0 
			iota(fa+1,fa+n+1,1);//初始化为 {1,2,3,4,5,6,...,n}(初始下标为 1)
			for(int j=1;j<n;j++){
				if(u[j]==i || v[j]==i) continue;
				merge(u[j],v[j]);
			}
			for(int j=1;j<=n;j++){
				if(i==j) continue;
				if(du[j]==1) sum[find(j)]--;//将度数为 1 的点所在集合点的个数减 1 
			}
			for(int j=1;j<=n;j++){
				if(i==j) continue;
				if(j==find(j) && sum[find(j)]>1) f=0;//判断 j 所在的集合个数不大于 1 
			}
			if(f) break; 
		}
		if(f) cout << "Yes" << endl;
		else cout << "No" << endl;
	}
	return 0;
}

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值