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

题目

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Σcson[i]acbc ,那么每当 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[N],tot;
LL a[N],b[N],fa[N],s[N],num[N];
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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值