bzoj 4730: Alice和Bob又在玩游戏 线段树合并&博弈论

      n居然是10w左右你敢信。。。连爆10+发OJ才发现。

     首先给每一个子树一个sg值;考虑当前点所在子树的sg,枚举第一次删哪一个点,那么剩下一些子树,这些子树的xor就是这个后继的值,然后求mex即可。

     考虑用线段树合并来维护。用线段树维护某一个要被删除的点在当前点的后继的值,那么更新就相当于全部抑或一个值,打标记维护即可。

     查询就贪心向左走即可。

AC代码如下:

#include
#define N 200005
#define M 4000005
#define clr(x,y) memset(x,0,sizeof(int)*(y+1))
using namespace std;
 
int n,m,tot,trtot,bin[25],fst[N],pnt[N],nxt[N],fa[N],sg[N],rt[N],ls[M],rs[M],sz[M],tg[M];
bool vis[N];
void add(int x,int y){
    pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void pushdn(int x,int k){
    if (tg[x]){
        if (tg[x]&bin[k-1]) swap(ls[x],rs[x]);
        tg[ls[x]]^=tg[x]; tg[rs[x]]^=tg[x]; tg[x]=0;
    }
}
void ins(int &k,int x,int y){
    sz[k=++trtot]=1; if (!y) return;
    if (x&bin[y-1]) ins(rs[k],x,y-1); else ins(ls[k],x,y-1);
}
int mrg(int x,int y,int k){
    if (!x || !y) return x|y;
    pushdn(x,k); pushdn(y,k);
    ls[x]=mrg(ls[x],ls[y],k-1); rs[x]=mrg(rs[x],rs[y],k-1);
    sz[x]=sz[ls[x]]+sz[rs[x]]+(k?0:1); return x;
}
void dfs(int x){
    int i,y,t=0; vis[x]=1;
    for (i=fst[x]; i; i=nxt[i]){
        y=pnt[i];
        if (y!=fa[x]){
            fa[y]=x; dfs(y); t^=sg[y];
        }
    }
    ins(rt[x],t,19);
    for (i=fst[x]; i; i=nxt[i]){
        y=pnt[i];
        if (y!=fa[x]){
            tg[rt[y]]=t^sg[y]; rt[x]=mrg(rt[x],rt[y],19);
        }
    }
    for (i=19,y=rt[x]; i; i--){
        pushdn(y,i);
        if (sz[ls[y]]200000){return 0;}
        int x,y;
        for (i=1; i<=m; i++){
            scanf("%d%d",&x,&y);
            add(x,y); add(y,x);
        }
        int ans=0;
        for (i=1; i<=n; i++)
            if (!vis[i]){
                dfs(i); ans^=sg[i];
            }
        puts((ans)?"Alice":"Bob");
        if (cas){
            memset(vis,0,sizeof(bool)*(n+1));
            clr(sg,n); clr(fa,n); clr(fst,n); clr(rt,n);
            clr(ls,trtot); clr(rs,trtot); clr(sz,trtot); clr(tg,trtot);
        }
    }
    return 0;
}


by lych

2016.12.31

你可能感兴趣的:(bzoj)