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太懒了,他懒得自己动手了。请写个程序帮帮他吧。

Solution


首先这个sg函数并没有什么用因为我们可以打表发现本题中的sg[x]=x
其次我在阅读题目的时候注意到了爆栈的字眼因此非常保守地写了bfs求dfs序,但是鱿鱼种种原因这题直接dfs是可行的
那么这就是一个求树上路径异或和是否为0+动态修改的问题,一般套路是树剖,然而两个log的复杂度并不是这题能承受的

一个直观的想法是记b[i]为点i到根路径上的异或和,而修改点x只影响x所在子树中的节点,并且这些节点一定是dfs序连续的一段。这个可以用树状数组搞搞

然而上面这个比较直观的方法并没有获得评测姬的认可,因此考虑另一种更加显然的思路。我比较着急去吃宵夜这里就不写了罢

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))
#define lowbit(x) ((x)&(-(x)))

const int N=500005;
const int E=1000005;

struct edge {int y,next;} e[E];

int ls[N],edCnt;
int a[N],b[N],c[N],fa[N][21],n,T;
int queue[N],size[N];
int dep[N],pos[N];

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

void add_edge(int x,int y) {
    e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge) {x,ls[y]}; ls[y]=edCnt;
}

void add(int x,int v) {
    for (;x<=n;x+=lowbit(x)) c[x]^=v;
}

int get(int x) {
    int ret=0;
    for (;x;x-=lowbit(x)) ret^=c[x];
    return ret;
}

void bfs(int st) {
    dep[st]=1; b[st]=a[st];
    int head=1,tail=0; queue[++tail]=st;
    while (head<=tail) {
        int now=queue[head++]; size[now]=1;
        rep(i,1,20) fa[now][i]=fa[fa[now][i-1]][i-1];
        for (int i=ls[now];i;i=e[i].next) {
            if (dep[e[i].y]) continue;
            dep[e[i].y]=dep[fa[e[i].y][0]=now]+1;
            queue[++tail]=e[i].y;
            b[e[i].y]=a[e[i].y]^b[now];
        }
    }
    drp(i,n,1) size[fa[queue[i]][0]]+=size[queue[i]];
    int last=(pos[1]=1);
    rep(i,2,n) {
        if (fa[queue[i]][0]!=fa[queue[i-1]][0]) last=pos[fa[queue[i]][0]];
        else last+=size[queue[i-1]];
        pos[queue[i]]=last+1;
    }
}

int get_lca(int x,int y) {
    if (dep[x]<=dep[y]) std:: swap(x,y);
    drp(i,20,0) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    if (x==y) return x;
    drp(i,20,0) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

int main(void) {
    // freopen("data.in","r",stdin);
    n=read();
    rep(i,1,n) a[i]=read();
    rep(i,2,n) add_edge(read(),read());
    bfs(1);
    // rep(i,1,n) add(pos[i],b[i]);
    rep(i,1,n) {
        add(pos[i],a[i]);
        add(pos[i]+size[i],a[i]);
    }
    T=read();
    while (T--) {
        char opt[2]; scanf("%s",opt);
        int x=read(),y=read();
        if (opt[0]=='Q') {
            int lca=get_lca(x,y);
            // int qx=get(pos[x])^get(pos[x]-1);
            // int qy=get(pos[y])^get(pos[y]-1);
            int qx=get(pos[x]);
            int qy=get(pos[y]);
            int ans=qx^qy^a[lca];
            if (ans) puts("Yes");
            else puts("No");
        } else {
            add(pos[x],a[x]^y);
            add(pos[x]+size[x],a[x]^y);
            a[x]=y;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值