做题记录,如有不对,欢迎指出。
题目大意:有一棵树有n个节点,每一个节点会被上成黑或者白两种不同的颜色,黑用B表示,白用W表示,题目会给你两组数组,其中a数组为所有的父节点,b数组为每一个节点上的色。例如:
ai:112335,bi:wbbwwbw,如图所示。
如果每一个子树(即以a数组里边元素为树根的树)黑白节点个数相同,则称这个树为平衡树。求:题目给你的树中,有多少子树是平衡树?
题意解析:题目给的条件对于建树描述很抽象,刚开始建树的时候我一时没想明白,后来才知道,其实这个树是这么建的:因为树根是1,对于a数组里每一个父节点,从2开始,一一对应,就和例子里的一样,第一个元素对应1,第二个对应2....(1-1,1-2.....),由此每一个a数组去重以后,就是所有子树的根。
大体思路:看到这一题,第一个思路就是dfs遍历去统计每个根黑白是否相同,显然暴力虽简单,但是肯定tle。其实这一题是树形dp的模板题,想求节点黑白的数量,只需要直到子树的黑白数量即可,这样,我们就得到了一个转移方程:
dp(黑,index)=dp(黑,上一个index)白色同理。
为了得到线性复杂度,和代码的简洁,我们采用前向星链表。
简单提一下,前向星链表在这一题主要维护3个元素,第一个是边,第二个是邻接边,第三个是头节点的邻接边。回头在我博客中会详细记录一下前向星的具体原理。
这样,通过dfs遍历到叶子节点,然后一步一步递归dp记录当前黑白数量,最后判断黑白是否相等即可。
主要代码:
void add(int x, int y)
{
e[cnt] = y;
ne[cnt] = head[x];
head[x] = cnt++;
}
void dfs(int a)
{
for (int i = head[a]; i != -1; i = ne[i])
{
int t = e[i];
dfs(t);
f[a][1] += f[t][1];
f[a][0] += f[t][0];
}
if (f[a][1] == f[a][0])ans++;
}
int main()
{
cin >> test;
while (test--)
{
memset(vis, 0, sizeof(vis));
memset(head, -1, sizeof(head));
memset(ne, 0, sizeof(ne));
memset(e, 0, sizeof(e));
memset(f, 0, sizeof(f));
cnt = 0;
cin >> n;
ans = 0;
for (int i = 2; i <= n; i++)
{
cin >> x;
add(x, i);
}
for (int i = 1; i <= n; i++)
{
cin >> c;
f[i][c == 'B'] = 1;
}
dfs(1);
cout << ans << endl;
}
}