[BZOJ3729]Gty的游戏/[JZOJ4759]石子游戏

来源:互联网 发布:21天学通c语言视频 编辑:程序博客网 时间:2024/05/29 08:52

题目大意

一棵树,初始时有n个节点,第i个节点上有ai个石子。给定m,有q个操作形如一下三种:
修改一个点的石子个数
在一个节点下面添加一个有若干个石子的新点
查询对点x为根的子树玩一下的游戏,先手是否必胜:每次选择一个点,将该点上不超过m个石子(不能为0)移动到父亲节点,不能移动者输。
1n,m,q5×104,ai[0,231)
保证任何时候节点的编号和个数都不超过5×104


题目分析

NIM的一些姿势

首先是如何解决移动不超过m个石子的问题,我们写一下SG函数转移式:sg(x)=mex{sg(xi)|0<im}。我们可以发现一个很显然的规律:sg(x)=x mod (m+1)
接着解决将石子移动到父亲的问题,其实这就是一个阶梯nim游戏的模型。将(子树)根节点看成第0层,然后我们只需要统计子树中所有奇数层的nim和(异或和)即可。为什么呢?因为偶数层的石子相当于“不存在的”,因为如果有一个人将一个石子从偶数层移动到奇数层,那么另一个人一定可以将它从奇数层移动到偶数层。而奇数层的石子一旦移动到偶数层,就相当于“消失了”。

各种乱搞

现在问题变为如何在支持插入和修改的情况下,维护子树的nim和(奇偶分两种存就好了)。
一个很显然的想法是使用DFS序,但是加入新点无疑是一个棘手的问题。其实我们可以用splay维护DFS序的区间nim和还有最小深度。一个点的子树的最后一个点的位置一定是该点后面第一个深度小于等于它的位置的前一个。随便维护一下就好了。时间复杂度O(nlog2n)
但是我splay不是很熟练,怎么办咧?骑士这题我们还可以定期重构。将所有修改操作先储存下来,如果达到了q个再暴力重构整棵树来得到我们想要的信息,查询时候除了在树中统计还要暴力那些存下来的个操作。
具体怎么实现呢?对一棵树,我们求出DFS序的前缀nim和数组。修改和插入操作我们先把它存下来,注意维护待插入点的树结构。查询的时候,如果查询的是未插入的点(被存下来的点),那我就可以直接暴力整棵子树(显然节点个数小于等于q)。如果查询的是树上的点,我们就先利用前缀nim和数组求出初始答案,然后枚举每一个修改操作,判一判对答案是否有影响,如果有就改一下。再枚举每一个待加入的点,依然判一判对答案是否有影响(看一看待加入点构成的树的根节点的父亲是否在子树内),改一改。具体细节就交给大家想一想吧。当存下来的东西达到q个的时候,我们将整棵树重新DFS一次,再次处理前缀nim和数组即可。
时间复杂度O(qq)


代码实现

出题人不遵守约定,点数可能超过5×104!!!

#include <algorithm>#include <iostream>#include <cstring>#include <cstdio>#include <cctype>#include <cmath>using namespace std;int read(){    int x=0,f=1;    char ch=getchar();    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();    return x*f;}const int N=100050;const int M=N<<1;int a[N],fa[N],last[N],DFN[N],size[N],nim[N][2],anc[N],add[N],mod[N][2],deep[N],tc[N];int n,q,lim,tot,mdcnt,bs,idx,lst,tid;int next[M],tov[M];int SG(int x){return x%(lim+1);}void insert(int x,int y){tov[++tot]=y,next[tot]=last[x],last[x]=tot;}void dfs(int x){    size[x]=1,DFN[x]=++idx,anc[x]=0;    nim[DFN[x]][deep[x]&1]=SG(a[x]),nim[DFN[x]][(deep[x]&1)^1]=0;    for (int i=last[x],y;i;i=next[i])        if ((y=tov[i])!=fa[x])            fa[y]=x,deep[y]=deep[x]+1,dfs(y),size[x]+=size[y];}void pre(){for (int i=1;i<=idx;i++) nim[i][0]^=nim[i-1][0],nim[i][1]^=nim[i-1][1];}int brute(int x,int og){    int ret=(((deep[x]&1)!=(deep[og]&1))&&x!=og)?SG(a[x]):0;    for (int i=last[x],y;i;i=next[i])        if ((y=tov[i])!=fa[x]) ret^=brute(y,og);    return ret;}bool win(int x){    tid++;    int sg;    if (anc[x]) sg=brute(x,x);    else    {        sg=nim[DFN[x]+size[x]-1][(deep[x]&1)^1]^nim[DFN[x]][(deep[x]&1)^1];        for (int i=1;i<=add[0];i++)            if (DFN[x]<=DFN[anc[add[i]]]&&DFN[anc[add[i]]]<=DFN[x]+size[x]-1&&((deep[x]&1)!=(deep[add[i]]&1)))                sg^=SG(a[add[i]]);        for (int p,i=mdcnt;i;i--)            if (tc[p=mod[i][0]]!=tid)            {                tc[p]=tid;                if (p!=x&&((deep[x]&1)!=(deep[p]&1))&&(anc[p]&&DFN[x]<=DFN[anc[p]]&&DFN[anc[p]]<=DFN[x]+size[x]-1||!anc[p]&&DFN[x]<=DFN[p]&&DFN[p]<=DFN[x]+size[x]-1)) sg^=SG(a[p])^SG(mod[i][1]);            }    }    return sg;}void rebuild(){    for (int i=1;i<=mdcnt;i++) a[mod[i][0]]=mod[i][1];    mdcnt=0,add[0]=0;    idx=0,deep[1]=1,dfs(1),pre();}int main(){    freopen("game.in","r",stdin),freopen("game.out","w",stdout);    n=read(),lim=read();    for (int i=1;i<=n;i++) a[i]=read();    for (int i=1,x,y;i<n;i++)    {        x=read(),y=read();        insert(x,y),insert(y,x);    }    idx=0,deep[1]=1,dfs(1),pre();    q=read(),bs=trunc(sqrt(q))+1,lst=0,tid=0;    for (int i=1,t,u,v,x;i<=q;i++)    {        t=read();        switch (t)        {            case 1:            {                x=read()^lst;                if (win(x)) printf("Yes\n"),lst++;                else printf("No\n");                break;            }            case 2:            {                u=read()^lst,x=read()^lst;                mod[++mdcnt][0]=u,mod[mdcnt][1]=x;                break;            };            case 3:            {                u=read()^lst,v=read()^lst,x=read()^lst;                fa[v]=u,a[v]=x,deep[v]=deep[u]+1,anc[v]=anc[u]?anc[u]:u,insert(u,v);                add[++add[0]]=v;            }        }        if (add[0]+mdcnt==bs) rebuild();    }    fclose(stdin),fclose(stdout);    return 0;}
0 0
原创粉丝点击