您的位置:首页 > 其它

JZOJ 5500. 【清华集训2017模拟12.10】营养餐

2017-12-09 17:10 260 查看

题目

JM 是 DY 的好朋友。为了感谢 JM 多年来对自己的关心,DY 决定请他吃一顿水果营养餐.

DY 有一棵有 n 个结点的树,结点 1 为根。树上每一个结点都长着许多水果,其中,结点 i 上有 ai 个水果,每个水果重 bi .

水果虽然好吃,但是这棵树非常脆弱! 一旦某结点的子结点上的水果总重量过大,树枝就会承受不住压力而断裂! 所以,随时需要保持任意一个结点 i:



(数据保证初始局面满足该条件).

JM 说, 谢谢你的这些水果, 不过我的内心毫无波动, 甚至还有点想博弈. 我们轮流来操作: 选择一个结点, 摘走若干个水果, 不可以不摘. 不可操作者输.

DY 说, 这毫无意义,我们都足够聪明,这个游戏一旦确定谁为先手,结果也确定了.

然而, 在前排吃瓜的你并不知道游戏的结果,所以,你得编写程序来知道谁会赢.

题解





看一波数据范围。

前两个点显然暴力。然而我不会,GG。

点3,4,b=0,所以a随便取,那么可以看作是n堆石子,每堆ai个的Nim游戏。

直接异或起来看等不等于0即可

点5,只有根节点到某个点为1的点的那条路径上的点a=1,其他a=0。

点6,设那两个a=1的点标号为x,y,只有根节点到lca(x,y)的点的a=2,lca(x,y) x,y的路径上的点a=1。那么可以发现,如果都剩下a=2的点,那么先手必败。判断lca(x,y) x,y的路径上的点的个数不含lca(x,y)的奇偶性即可。

点7,设numi=ai−Σc∈son[i]ac∗bc,那么每当ai减少k,numi减少k,numfai增加k。

那么介绍一个新的博弈模型,阶梯式Nim。

将奇数深度的点的num的集合为A,偶数深度的点的num的集合为B。根节点深度为1。

如果操作奇数深度的非根节点,那么A中石子移到了B,操作偶数深度的非根节点,那么B中石子移到了A,操作根节点,那么A中石子移走。

所以当必败方将B中的石子移到A中时,对方也可以将移进B中的相同个数的石子移进A。

可以认为B中的石子是没有用的。

那么如果b=0,则i对fai不产生影响,所以i的子树可以看做一棵独立的子树。

无论b为何值,只要是放入B集合的石子都没有用了。

所以计算出森林中深度为奇数的num,异或起来判断是否等于0即可。

心得

①部分分的解法与正解的解法一定有很大出入!!!

②一定要找到这样的方法:必败方给出一定的操作,然后对方能够以相同的操作让必败方回到必败态,那么此博弈方案可行。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 50005
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{
int to,next;
};note edge[N*2];
int head
,tot;
LL a
,b
,fa
,s
,num
;
LL i,j,n,m,_,sum;
LL u,v,step;
bool p;
LL read(){
LL res=0,fh=1;char ch;
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')fh=-1,ch=getchar();
while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar();
return res*fh;
}
void lb(LL x,LL y){edge[++tot].to=y;edge[tot].next=head[x];head[x]=tot;}
void dg(int x){
for(int i=head[x];i;i=edge[i].next)
if(fa[x]!=edge[i].to){
fa[edge[i].to]=x;
if(b[edge[i].to]==0)s[edge[i].to]=1;
else s[edge[i].to]=s[x]^1;
dg(edge[i].to);
num[x]-=a[edge[i].to]*b[edge[i].to];
}
}
int main(){
_=read();
while(_--){
tot=0;
memset(edge,0,sizeof(edge));
memset(head,0,sizeof(head));
memset(fa,0,sizeof(fa));
memset(s,0,sizeof(s));
n=read();
fo(i,1,n)a[i]=read(),num[i]=a[i];
fo(i,1,n)b[i]=read();
fo(i,1,n-1){
u=read(),v=read();
lb(u,v),lb(v,u);
}
s[1]=1;
dg(1);
sum=0;
fo(i,1,n)if(s[i])sum^=num[i];
if(sum)printf("YES\n");else printf("NO\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: