C. Bakry and Partitioning(树 + 异或和)

Problem - C - Codeforces

 Bakry遇到了一个问题,但由于他懒得解决它,他请求你的帮助。给你一个有n个节点的树,第i个节点的值为a,对于从1到n的每一个i都赋值给它。提醒一下,有n个节点的树是有n-1条边的连通图。你想从树中删除至少1条边,但最多k-1条边,这样下面的条件就成立了:对于每个连接的组件,计算其中节点值的逐位异或。然后,对于所有连接的组件,这些值必须相同。这个条件有可能实现吗?输入每个测试包含多个测试用例。第一行包含测试用例的数量t (1 <t< 5 - 104)。测试用例的描述每个测试用例的第一行包含两个整数n和k (2 < k < n <105)。每个测试用例的第二行包含n个整数a1,a2,。, an (1 < a;≤10°)。接下来n-1行的第i行包含两个整数u和v (1 < ui, vi < n, ui vi),这意味着在节点ui和v之间有一条边。可以保证给定的图是树。它保证所有测试用例的n和不超过2- 105。输出对于每个测试用例,您都应该输出一个字符串。如果你可以根据上面写的条件删除边,输出“YES”(不带引号)。否则,输出“NO”(不带引号)。您可以在任何情况下打印“YES”和“NO”的每个字母(大写或小写)。

Example

input

Copy

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

output

Copy

NO
YES
NO
YES
NO

题解:

题目问我们是否可以移除几条边,使各部分异或和相等
我们可以从异或的性质入手

如果所有点异或和为0,说明中间存在两个相等部分异或为0(两个相等的数异或为0)x ^ x = 0

若不为0, x ^ x ^ x = x

3 3 3

有三个或三个以上部分异或值 = 总体异或值

接着我们就dfs寻找,树中出现了几次这样的值,注意每次找到要清空那个树根的值

#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
typedef long long ll;
#define int long long
typedef pair<int,int> PII;
int a[100050];
int cnt;
int b[100050];
vector<int> p[100050];
int s = 0;
void dfs(int x,int fa)
{
	b[x] = a[x];
	for(auto ne:p[x])
	{
		if(ne == fa)
		continue;
		dfs(ne,x);
		b[x] ^= b[ne];
	} 
	if(b[x] == s)
	{
		cnt++;
		b[x] = 0;
	}
}
void solve()
{
	int n,k;
	cin >> n >> k;
	cnt = 0;
	s = 0;
	for(int i = 1;i <= n;i++)
	{
		p[i].clear();
		b[i] = 0;
	}
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i];
		s ^= a[i];
	}
	for(int i = 1;i < n;i++)
	{
		int x,y;
		cin >> x >> y;
		p[x].push_back(y);
		p[y].push_back(x);
	}
	if(s == 0)
	{
		cout <<"YES\n";
	}
	else
	{
		dfs(1,-1);
		if(k - 1 >= 2&&cnt >= 2)
		{
			cout <<"YES\n";
		}
		else
		{
			cout <<"NO\n";
		}
	}
	
}
signed main() 
{
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
	int t = 1;
	cin >> t;
//    scanf("%lld",&t);
	while (t--) 
	{
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值