链接:https://ac.nowcoder.com/acm/contest/3005/F
来源:牛客网
题目描述:
现有一个 n 个点,n-1条边组成的树,其中 1 号点为根节点。
牛牛和牛妹在树上玩游戏,他们在游戏开始时分别在树上两个不同的节点上。
在游戏的每一轮,牛牛先走一步,而后牛妹走一步。他们只能走到没有人的空节点上。如果谁移动不了,就输掉了游戏。现在牛牛和牛妹决定随机选择他们分别的起点,于是他们想知道,有多少种游戏开始的方式,使得牛牛存在一种一定获胜的最优策略。
两种开始方式相同,当且仅当在两种开始方式中牛牛,牛妹的开始位置是分别相同的,否则开始方式就被视作不同的。
输入描述:
第一行输入为一个整数 n,代表树的点数。
第二行n-1个整数p2,p3,…,pn,分别代表2,3,…,n号点的父节点编号。
输出描述:
一行一个整数,代表答案。
输入样例:
30
1 1 2 1 2 1 3 2 3 4 2 3 1 2 3 4 2 4 5 6 3 4 12 12 12 13 13 13 13
输出样例:
428
核心思想:
假定每条边的长度为1。
若两人之间的距离为1(即在一条边的两个端点上),设接下来是A行动,则A就会输掉,因为A只能背向B走,然后B向着A步步紧逼即可将A逼的无路可走。
扩展一下,当两人直接的距离为奇数时,先走的人败,为偶数时,先走的人胜。
此题转化为:求树上距离为偶数的有序点对数。
树形dp即可。
代码如下:
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e6+20;
int cs,head[N];
ll ou[N],ji[N],ans;
struct node{
int y,ne;
}side[N];
void add(int x,int y)
{
side[cs].y=y;
side[cs].ne=head[x];
head[x]=cs++;
return;
}
void dfs(int x)
{
ll sumou=0,sumji=0;
for(int i=head[x];i!=-1;i=side[i].ne)
{
int y=side[i].y;
dfs(y);
sumou+=ou[y];
sumji+=ji[y];
}
for(int i=head[x];i!=-1;i=side[i].ne)
{
int y=side[i].y;
ans+=ji[y]*(sumji-ji[y]);
ans+=ou[y]*(sumou-ou[y]);
}
ans+=sumji*2;
ou[x]=sumji+1;
ji[x]=sumou;
return;
}
int main()
{
int n,x;
cin>>n;
for(int i=0;i<N;i++)
head[i]=-1;
for(int i=2;i<=n;i++)
{
scanf("%d",&x);
add(x,i);
}
dfs(1);
cout<<ans<<endl;
return 0;
}