【XSY2716】营养餐 博弈论

题目描述

  给你一棵有根树,每个点有两个属性(a,b)

  两人轮流操作,每次要减小一个点的(a)值,要求

[a_xgeqsum_{iin child(x)}a_ib_i ]

  保证初始状态满足这个要求。

  (sum nleq 5 imes {10}^5)

题解

  令

[s_x=a_x-sum_{iin child(x)}a_ib_i ]

  每次操作相当于减小(s_x),把(s_{f_x})加上减小的值$ imes $$b_x$。

  当(b_x=0)(x)(f_x)没有影响,可以把(x)视为根。

  把原树划分成森林后做阶梯博弈即可。

  计算出所有深度为(x)的点的(s_x)异或和,如果非零则先手胜,否则后手胜。

  阶梯博弈:所有深度为偶数的点的信息是没有用的。如果把某一个偶数层的点的值挪到奇数层的点上,对手可以再把这些值挪到偶数层的点上。所以最好情况都不会对自己有利,就不会这么决策。

  时间复杂度:(O(n))

代码

#include<cstdio>
#include<cstring>
using namespace std;
struct graph
{
	int v[100010];
	int t[100010];
	int h[50010];
	int n;
	void add(int x,int y)
	{
		n++;
		v[n]=y;
		t[n]=h[x];
		h[x]=n;
	}
	void init()
	{
		memset(h,0,sizeof h);
		n=0;
	}
};
graph g;
int f[100010];
int ban[100010];
int s[100010];
int a[100010];
int b[100010];
void dfs(int x,int fa)
{
	f[x]=fa;
	s[x]=a[x];
	int i;
	for(i=g.h[x];i;i=g.t[i])
		if(g.v[i]!=fa)
		{
			dfs(g.v[i],x);
			s[x]-=a[g.v[i]]*b[g.v[i]];
		}
}
int ans;
void dfs2(int x,int d)
{
	if((d&1))
		ans^=s[x];
	int i;
	for(i=g.h[x];i;i=g.t[i])
		if(g.v[i]!=f[x]&&!ban[g.v[i]])
			dfs2(g.v[i],d+1);
}
void solve()
{
	int n;
	scanf("%d",&n);
	int i,x,y;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(i=1;i<=n;i++)
		scanf("%d",&b[i]);
	g.init();
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		g.add(x,y);
		g.add(y,x);
	}
	for(i=1;i<=n;i++)
		ban[i]=0;
	for(i=1;i<=n;i++)
		if(!b[i]||i==1)
			ban[i]=1;
	dfs(1,0);
	ans=0;
	for(i=1;i<=n;i++)
		if(ban[i])
			dfs2(i,1);
	if(ans)
		printf("YES
");
	else
		printf("NO
");
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
#endif
	int t;
	scanf("%d",&t);
	while(t--)
		solve();
	return 0;
}
原文地址:https://www.cnblogs.com/ywwyww/p/8513549.html