【XSY2716】营养餐 博弈论

题目描述

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

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

\[a_x\geq\sum_{i\in child(x)}a_ib_i \]

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

  \(\sum n\leq 5\times {10}^5\)

题解

  令

\[s_x=a_x-\sum_{i\in child(x)}a_ib_i \]

  每次操作相当于减小\(s_x\),把\(s_{f_x}\)加上减小的值$\times $$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\n");
	else
		printf("NO\n");
}
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;
}
posted @ 2018-03-06 11:43  ywwyww  阅读(190)  评论(0)    收藏  举报