[BZOJ2819] Nim

题目描述

Description

著名游戏设计师vfleaking,最近迷上了Nim。普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取。谁不能取谁输。这个游戏是有必胜策略的。于是vfleaking决定写一个玩Nim游戏的平台来坑玩家。
为了设计漂亮一点的初始局面,vfleaking用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1,2,3,4,...n,在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达。然后他不停地进行如下操作:
1.随机选两个堆v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略,如果有,vfleaking将会考虑将这些石子堆作为初始局面之一,用来坑玩家。
2.把堆v中的石子数变为k。
由于vfleaking太懒了,他懒得自己动手了。请写个程序帮帮他吧。

Input

第一行一个数n,表示有多少堆石子。
接下来的一行,第i个数表示第i堆里有多少石子。
接下来n-1行,每行两个数v,u,代表v,u间有一条边直接相连。
接下来一个数q,代表操作的个数。
接下来q行,每行开始有一个字符:
如果是Q,那么后面有两个数v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略。
如果是C,那么后面有两个数v,k,代表把堆v中的石子数变为k。
对于100%的数据:
1≤N≤500000, 1≤Q≤500000, 0≤任何时候每堆石子的个数≤32767
其中有30%的数据:
石子堆组成了一条链,这3个点会导致你DFS时爆栈(也许你不用DFS?)。其它的数据DFS目测不会爆。
注意:石子数的范围是0到INT_MAX

Output

对于每个Q,输出一行Yes或No,代表对询问的回答。

Sample Input

【样例输入】

5
1 3 5 2 5
1 5
3 5
2 5
1 4
6
Q 1 2
Q 3 5
C 3 7
Q 1 2
Q 2 4
Q 5 3

Sample Output

Yes
No
Yes
Yes
Yes

题目分析

首先 Nim先手必赢得条件是所有石子数异或和不为0
那么题目要求 单点修改 询问两点路径异或和
这明显是树剖嘛QAQ 但是出题人告诉我们爆栈
那...写广搜树剖嘛

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
int n,m;
char s[10];
int val[500010];
int head[500010],to[500010*2],net[500010*2];
int deep[500010],size[500010],number[500010],son[500010],top[500010],fa[500010],value[500010];
int tot,cnt,que[500100];
int read()
{
    char ch=getchar();
    int sum=0;
    while(!(ch>='0'&&ch<='9')) ch=getchar();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=getchar();
    return sum;
}
void add(int x,int y)
{
    net[++tot]=head[x],head[x]=tot,to[tot]=y;
}
void bfs()
{
    int l=0,r=0;
    que[++r]=1;
    while(l!=r)
    {
        int x=que[++l];     
        deep[x]=deep[fa[x]]+1;
        size[x]=1;
        for(int i=head[x];i;i=net[i])
            if(to[i]!=fa[x]) fa[to[i]]=x,que[++r]=to[i];
    }
    for(int i=n;i>=1;i--) 
    {
        int x=que[i];
        size[fa[x]]+=size[x];
        if(size[x]>size[son[fa[x]]]) son[fa[x]]=x;
    }
    for(int i=1;i<=n;i++) 
    {
        int x=que[i];
        if(son[fa[x]]==x) top[x]=top[fa[x]];
        else
        {
            top[x]=x;
            for(int j=x;j;j=son[j]) number[j]=++cnt,value[cnt]=val[j];
        }
    }
}
struct your
{
    int x,y,sum;
}a[500010*4];
void build(int dx,int dy,int num)
{
    a[num].x=dx,a[num].y=dy;
    if(dy==dx)
    {
        a[num].sum=value[dx];
        return ;
    }
    int mid=(dx+dy)>>1;
    build(dx,mid,num<<1),build(mid+1,dy,num<<1|1);
    a[num].sum=a[num<<1].sum^a[num<<1|1].sum;
}
void update(int dx,int c,int num)
{
    if(dx==a[num].x&&dx==a[num].y)
    {
        a[num].sum=c;
        return ;
    }
    int mid=(a[num].x+a[num].y)>>1;
    if(dx<=mid) update(dx,c,num<<1);
    else update(dx,c,num<<1|1);
    a[num].sum=a[num<<1].sum^a[num<<1|1].sum;
}
int ask(int dx,int dy,int num)
{
    if(a[num].x==dx&&a[num].y==dy) return a[num].sum;
    int mid=(a[num].x+a[num].y)>>1;
    if(dx>mid) return ask(dx,dy,num<<1|1);
    else if(dy<=mid) return ask(dx,dy,num<<1);
    else return ask(dx,mid,num<<1)^ask(mid+1,dy,num<<1|1);
}
int find(int x,int y)
{
    int sum=0;
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]]) swap(x,y);
        sum^=ask(number[top[x]],number[x],1);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])  swap(x,y);
    sum^=ask(number[x],number[y],1);
    return sum;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++) val[i]=read();
    for(int i=1;i<n;i++) 
    {
        int x,y;
        x=read(),y=read();
        add(x,y),add(y,x);
    }
    bfs();
    build(1,n,1);
    m=read();
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%s",&s[0]);
        x=read(),y=read();
        if(s[0]=='C') update(number[x],y,1);
        else printf("%s\n",find(x,y)==0?"No":"Yes");
    }
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注