题解 | #牛牛的树#

牛牛的树

https://ac.nowcoder.com/acm/problem/21629

解题思路

首先澄清一点,不「重要」的边可以是亮着的,也可以是灭着的,这个题目里没说,只能通过样例 #1 去理解,希望后续有人来修复一下。

考虑从所有的叶子结点开始向上考虑。

对于一个点到自己父亲的边,(u,fau)(u,fa_u),我们考虑:

  1. 如果这条边不「重要」,我们不需要管它的状态;
  2. 如果这条边「重要」,并且目前它是灭着的,我们就需要翻转这条边的状态。

另外,考虑一些相连的边可以放在一起翻转,下面以样例 #2 为例:

首先我们考虑到结点 66,它和它父亲之间的边「重要」,所以需要翻转,那么我们标记一下这条链需要翻转。

另外我们发现例如 (1,4)(1,4) 之间的边翻转两次等于没有翻转,所以我们自下向上考虑的策略是对的,这样可以保证需要翻转的边都被翻转。

还有一个不那么显然的事情是:例如我们考虑的是 3,1,4,63,1,4,6 这条链,实际上可以理解成 3,1,03,1,06,4,1,06,4,1,0 这两条链拼起来而成,上方重叠的可以忽略不计(两次翻转等于没有翻转)。

最后考虑每个点的影响传到了根结点,根结点把这些链两两配对,假设这种有「影响」的链有 xx 条,那么最后配出来的对数就应该是 x2\left\lceil\dfrac{x}{2}\right\rceil

#include<cstdio>
#define int long long
int init(){
	char c = getchar();
	int x = 0, f = 1;
	for (; c < '0' || c > '9'; c = getchar())
		if (c == '-') f = -1;
	for (; c >= '0' && c <= '9'; c = getchar())
		x = (x << 1) + (x << 3) + (c ^ 48);
	return x * f;
}
void print(int x){
	if (x < 0) x = -x, putchar('-');
	if (x > 9) print(x / 10);
	putchar(x % 10 + '0');
}
const int N = (int) 2e5 + 5;
int a[N], b[N], c[N], fa[N]; // c 数组表示该状态是否翻转,同时记录孩子结点传上来的状态和自身的影响
signed main(){
	int n = init();
	for (int i = 2; i <= n; ++i)
		fa[i] = 1 + init();
	for (int i = 2; i <= n; ++i)
		scanf("%1d", a + i);
	for (int i = 2; i <= n; ++i)
		scanf("%1d", b + i);
	int ans = 0;
	for (int i = n; i >= 2; --i)
		if (b[i] && !(a[i] ^ c[i])) // 需要翻转,且目前状态是不亮
            ++ans, c[fa[i]] ^= c[i] ^ 1;
	    else c[fa[i]] ^= c[i]; // 把影响一路向上传递
	print((ans + 1) >> 1), putchar('\n');
}
全部评论

相关推荐

offer多多的六边形战士很无语:看了你的博客,感觉挺不错的,可以把你的访问量和粉丝数在简历里提一下,闪光点(仅个人意见)
点赞 评论 收藏
分享
shtdbb_:还不错,没有让你做了笔试再挂你
点赞 评论 收藏
分享
2 1 评论
分享
牛客网
牛客企业服务