牛客3005F-树上博弈-树形dp

链接: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;
}
发布了144 篇原创文章 · 获赞 135 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Nothing_but_Fight/article/details/104268200