https://ac.nowcoder.com/acm/contest/3005/F
现有一个 n 个点,n-1条边组成的树,其中 1 号点为根节点。
牛牛和牛妹在树上玩游戏,他们在游戏开始时分别在树上两个不同的节点上。
在游戏的每一轮,牛牛先走一步,而后牛妹走一步。他们只能走到没有人的空节点上。如果谁移动不了,就输掉了游戏。现在牛牛和牛妹决定随机选择他们分别的起点,于是他们想知道,有多少种游戏开始的方式,使得牛牛存在一种一定获胜的最优策略。
两种开始方式相同,当且仅当在两种开始方式中牛牛,牛妹的开始位置是分别相同的,否则开始方式就被视作不同的。
思路
首先考虑输的情况,因为是一棵树 ,而且只能走没走过的点,输的时候就是接下来唯一能走的点被走过了或者牛妹在那里,因为两个人都会做出最优的策略,所以只有对方才会卡到自己的下一步的位置,也就是说如果走了一段时间他们的距离是1了,并且该牛牛走了, 牛牛就输了,向外延伸可以发现,他们两个是轮流走的,每次都是走1,所以距离每次变化+1或者-1,两个人都走过后距离+0或者+2或者-2,总的来说都是偶数,也就是说他们的距离变化不影响距离的奇偶性,如果一开始奇数,变化一段时间后肯定还是奇数,最后就会变成1,牛牛输了,同理,一开始偶数那么随着一直变化,最后会变成2,然后牛牛走完变成1,牛牛就赢了,只需要计算一下距离为偶数的点对个数即可。通过dfs计算每个点的深度,统计深度为奇数和偶数的点,奇数+奇数=偶数,偶数+偶数=偶数,所以最后结果就是(奇数点个数)*(奇数点个数-1)+(偶数点个数)*(偶数点个数-1)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
const int N = 1e6 + 10;
int root[N];
int f[N];
int head[N], nex[N], to[N], cnt;
void add(int a, int b) {
++cnt;
to[cnt] = b;
nex[cnt] = head[a];
head[a] = cnt;
}
void dfs(int now) {
for (int i = head[now]; i; i = nex[i]) {
int y = to[i];
f[y] = f[now] + 1;
dfs(y);
}
}
int main()
{
ios::sync_with_stdio(0);
cin >> n;
memset(head, 0, sizeof head);
memset(nex, 0, sizeof nex);
cnt = 0;
for (int i = 2; i <= n; i++) {
cin >> root[i];
add(root[i], i);
}
f[1] = 0;
dfs(1);
ll odd, even;
odd = 0;
even = 0;
for (int i = 1; i <= n; i++) {
if (f[i] & 1)odd++;
else even++;
}
ll res = odd * (odd - 1) + even * (even - 1);
printf("%lld\n", res);
return 0;
}