Codeforces1592C Bakry and Partitioning (思维)

99 篇文章 2 订阅
20 篇文章 0 订阅

题目链接: Bakry and Partitioning

大致题意

给定一棵有 n n n个节点的树, 第 i i i个节点有权值 w i w_i wi.

问你能否把这棵树分成 [ 2 , k ] [2, k] [2,k]个部分, 使得每部分中所有节点的异或和相同.

解题思路

思维 (至暗时刻, 我比赛时在干什么啊!!)

首先考虑到如果整棵树上的所有节点, 异或和 s u m = = 0 sum == 0 sum==0, 那么我们一定可以把整棵树拆成两部分, 满足题意.

我们可以选择任一叶子结点 x x x, 然后删除该节点到其父亲的边, 这样两部分的结点异或和一定均为 w x w_x wx


如果整棵树上的所有节点异或和 s u m ≠ 0 sum \ne 0 sum=0, 那么我们一定要把整棵树拆成奇数(≥3)个部分, 使得每部分的异或和为 s u m sum sum.

至于为什么每部分的异或和一定为 s u m sum sum.

我们不妨设整棵树被分成了 3 3 3部分, 每部分的异或和为 x x x.

则有: x ⊕ x ⊕ x = = s u m x \oplus x \oplus x == sum xxx==sum, 由于 x ⊕ x = = 0 x \oplus x == 0 xx==0, 则 x = = s u m x == sum x==sum.

如果要把整棵树分成奇数(≥3)个部分, 那么分成 3 3 3个部分一定是最优的.

当我们把整棵树拆分成 n u m num num( n u m num num为奇数)个部分, 由于原先是树是连通的, 那么拆分后, 每个部分也必然会有对应原先和它连通的部分. 因此我们可以不断合并 3 3 3个连通的部分, 使得 n u m = 3 num = 3 num=3.

例如题中样例, 有 5 5 5个节点, 每个节点的权值都是3. Note中给出了分成5个部分的答案, 其实也可以分成3个部分.


接下来考虑如何拆分.

我们应该求出这棵树最多能拆分成多少个部分, 使得每部分的异或和为 s u m sum sum. 那么在进行拆分时, 我们每次拆分出的一定是一棵子树, 一旦某棵最小的子树满足条件, 我们把这棵子树删除, 继续拆分即可.

如果拆分出的不是一颗子树, 那么当前节点的子节点不再和其他节点连通, 会导致该部分的异或和不为 s u m sum sum.

我们可以通过dfs的方式来得到最大拆分数量.

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
int w[N];
vector<int> edge[N];

int all = 0; // 统计最大能拆分出多少个部分
int dfs(int x, int fa, const int target) { // 返回当前子树异或和
	int now = w[x]; // 统计当前子树异或和
	for (auto& to : edge[x]) {
		if (to == fa) continue;
		now ^= dfs(to, x, target);
	}

	if (now == target) { //该子树满足条件, 拆分部分++, 并删除该子树
		all++;
		return 0;
	}
	return now;
}
int main()
{
    int t; cin >> t;
    while (t--) {
    	int n, m; scanf("%d %d", &n, &m);

    	int sum = 0;
    	rep(i, n) scanf("%d", &w[i]), edge[i].clear(), sum ^= w[i];
    	rep(i, n - 1) {
    		int a, b; scanf("%d %d", &a, &b);
    		edge[a].push_back(b);
    		edge[b].push_back(a);
    	}

    	if (!sum) {
    		puts("YES");
    		continue;
    	}

    	all = 0;
    	dfs(1, 0, sum);

    	if (all >= 3 and m >= 3) puts("YES");
    	else puts("NO");
    }

    return 0;
}

END

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值