题解 | #牛牛的树#
牛牛的树
https://ac.nowcoder.com/acm/problem/21629
解题思路
首先澄清一点,不「重要」的边可以是亮着的,也可以是灭着的,这个题目里没说,只能通过样例 #1 去理解,希望后续有人来修复一下。
考虑从所有的叶子结点开始向上考虑。
对于一个点到自己父亲的边,,我们考虑:
- 如果这条边不「重要」,我们不需要管它的状态;
- 如果这条边「重要」,并且目前它是灭着的,我们就需要翻转这条边的状态。
另外,考虑一些相连的边可以放在一起翻转,下面以样例 #2 为例:
首先我们考虑到结点 ,它和它父亲之间的边「重要」,所以需要翻转,那么我们标记一下这条链需要翻转。
另外我们发现例如 之间的边翻转两次等于没有翻转,所以我们自下向上考虑的策略是对的,这样可以保证需要翻转的边都被翻转。
还有一个不那么显然的事情是:例如我们考虑的是 这条链,实际上可以理解成 和 这两条链拼起来而成,上方重叠的可以忽略不计(两次翻转等于没有翻转)。
最后考虑每个点的影响传到了根结点,根结点把这些链两两配对,假设这种有「影响」的链有 条,那么最后配出来的对数就应该是 。
#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');
}