D. Iris and Game on the Tree (Codeforces Round 969 Div. 2)

D. Iris and Game on the Tree

Iris 有一棵树,其根节点为 1 1 1 。每个顶点的值为 0 \mathtt 0 0 1 \mathtt 1 1

让我们考虑树的一片叶子(顶点 1 1 1 永远不会被视为叶子)并定义其权重。构造一个由从根节点开始到该叶子节点结束的路径上顶点的值组成的字符串。然后,叶子节点的权重是其中子字符串 10 \mathtt{10} 10 01 \mathtt{01} 01 的出现次数之差。

以下面的树为例。绿色顶点的值为 1 \mathtt 1 1 ,而白色顶点的值为 0 \mathtt 0 0

  • 让我们计算叶子节点 5 5 5 的权重:形成的字符串为 10110 \mathtt{10110} 10110 。子字符串 10 \mathtt{10} 10 的出现次数为 2 2 2 ,子字符串 01 \mathtt{01} 01 的出现次数为 1 1 1 ,因此差值为 2 − 1 = 1 2 - 1 = 1 21=1
  • 让我们计算叶子节点 6 6 6 的权重:形成的字符串为 101 \mathtt{101} 101 。子字符串 10 \mathtt{10} 10 的出现次数为 1 1 1 ,子字符串 01 \mathtt{01} 01 的出现次数为 1 1 1 ,因此差值为 1 − 1 = 0 1 - 1 = 0 11=0

树的得分定义为树中权重非零的叶子的数量。

但有些顶点的值尚未确定,将以 ? \texttt{?} ? 的形式提供给您。填空太无聊了,所以 Iris 打算邀请 Dora 玩游戏。每次轮到一位女孩选择剩余值为 ? \texttt{?} ? 的顶点之一,并将其​​值更改为 0 \mathtt{0} 0 1 \mathtt{1} 1Iris 先走。游戏持续进行,直到树中没有剩余值为 ? \mathtt{?} ? 的顶点。Iris 的目标是最大化树的得分,而 Dora 的目标是最小化得分。

假设两个女孩都发挥出最佳水平,请确定树的最终得分。

思路:

很容易想到,一条从根到叶子的路径是否有得分,只取决于根节点与叶子节点是否相同。相同则无贡献,不同则有贡献。
那么双方博弈就很简单了,只用看根节点和叶子的情况,先手抢占有利的一方即可。
只有一种特殊情况下需要考虑到路径节点上的’?‘,即当根节点为‘?’,且叶子节点中‘0’、‘1’的数量相等时(也可以都为0),此时双方处于平衡状态,在根节点和叶子节点上先手的一方反而会成为后手,所以Iris需要利用中间路径上的‘?’来拉扯一下,如果中间路径上的’?'数量为奇数,那么通过拉扯就可以使先后手转换,那么Iris就不会亏掉这一分。

代码:
#include <bits/stdc++.h>
#define endl '\n'
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
using namespace std;

int main() {
	int t;
	cin >> t;
	while (t--) {
		int n;
		cin >> n;
		vector<vector<int>> g(n + 1);
		for (int i = 1; i < n; ++i) {
			int u, v;
			cin >> u >> v;
			g[u].push_back(v);
			g[v].push_back(u);
		}
		string s;
		cin >> s;

		int zeros = 0, ones = 0, ques = 0;
		int allq = std::count(s.begin(), s.end(), '?'); // 统计输入字符串中?的数量
//		cout<<"allq="<<allq<<endl;
		// 遍历所有节点,统计叶子节点值和?的数量
		for (int i = 2; i <= n; ++i) {
			if (g[i].size() == 1) { // 如果节点是叶子
				if (s[i - 1] == '0') {
					zeros++;
				} else if (s[i - 1] == '1') {
					ones++;
				} else {
					ques++;
				}
			}
		}
//		cout<<"ques="<<ques<<endl;
		int ans = 0;
		// 根据根节点的值决定初始的ans
		if (s[0] == '0') {
			ans = ones; // 如果根是0,则非0的叶子数即为得分
		} else if (s[0] == '1') {
			ans = zeros; // 如果根是1,则非1的叶子数即为得分
		} else { // 根节点是?
			ans = max(zeros, ones); // 取较多的那个,因为根会变成较多的那个
			if (ones == zeros && (allq - ques - 1) % 2 == 1 && (allq - ques - 1) > 0) {
//				cout<<allq - ques - 1<<" ok"<<endl;
				ques++;
			}
			ques--;

		}

		ans += (ques + 1) / 2;
//		cout << ques << " ";
		cout << ans << '\n';
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值