您的位置:首页 > 其它

bzoj2819 Nim

2015-12-23 14:56 369 查看
http://www.lydsy.com/JudgeOnline/problem.php?id=2819
题意:对一棵树单点修改和询问链上各点权作为Nim游戏的初始局面先手是否有必胜策略。n,q<=500,000。
众所周知,一个Nim游戏的初始局面是先手必胜的当前仅当各堆石子数的异或结果不为0,因此题目等价于单点修改和询问链异或结果。
先考虑没有修改的情况,由于异或运算具有可加减性,很容易想到这个做法:任取一点为根,用dfs预处理好每个点到根的路径上各点的异或结果f[]。
当求点x到点y的路径上各点的异或结果,只需先求出它们的最近公共祖先u,那么f[x]^f[y]^f[u]^v[u]就是答案,其中v[]表示点权。
也就是说,求链异或结果只需知道f[]和v[]。v[]的维护就简单了,而f[]的维护则显得有些棘手。
注意到,点x的权值改变能够影响f[y]的值,当且仅当x是y的祖先,即y在以x为根的子树中。
也就是说,修改点权后,我们要维护f[]值的点都在以被修改点为根的子树中。而且,是将子树中的每个点都异或上一个相同的值。
如果我们记录每个点在dfs时被访问的时间戳,即dfs序,那么我们可以发现,每个子树中的所有结点对应的dfs序恰好是连续的一段。
也就是说,我们能够将修改子树转化为修改区间a[l...r]。同理,询问f[]也只要询问其dfs序对应的值a[]。
至于区间修改和单点询问,这很容易用线段树或差分序列的树状数组实现。
当然,这题用树链剖分做也是可行的,不过可能会需要降低一下常数,且编程复杂度也提高了。
代码:
#include<cstdio>

#include<iostream>

#include<cstdlib>

#include<ctime>

#define rpt(i,l,r) for(i=l;i<=r;i++)

#define rpd(i,r,l) for(i=r;i>=l;i--)

#define M 500005

using namespace std;

int v[M],a[M]={0};

int n,i,j,k,xx,yy;

char ss[2];

int x[M],y[M],s[M]={0},t[M*2];

int f[M][19],l[M],r[M],d[M],q[M],p[M];

int tot=0;

int source;

int read(){

intx=0,f=1;char ch=getchar();

while(ch<'0'||ch>'9'){if(ch=='0')f=-1;ch=getchar();}

while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}

returnx*f;

}

int lowbit(int x){

return x&(x^(x-1));

}

int sum(int x){

if(x) return a[x]^sum(x-lowbit(x));

else return 0;

}

void modify(int x,int y){

if(x>n) return;

a[x]^=y;

modify(x+lowbit(x),y);

}

void dfs(int x){

l[x]=++tot;p[tot]=x;

for(int i=s[x-1]+1;i<=s[x];i++)if(f[x][0]!=t[i]){f[t[i]][0]=x;d[t[i]]=d[x]+1;q[t[i]]=q[x]^v[t[i]];dfs(t[i]);}

r[x]=tot;

}

int lca(int x,int y){

int k,i;if(d[x]>d[y]) k=x,x=y,y=k;

k=d[y]-d[x];rpt(i,0,18) if(k&(1<<i)) y=f[y][i];

if(x==y) return x;

rpd(i,18,0) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];

return f[x][0];

}

int main(){

freopen("2819in.txt","r",stdin);

freopen("2819myout.txt","w",stdout);

n=read();v[0]=0;rpt(i,1,n) v[i]=read();

rpt(i,1,n-1){x[i]=read();y[i]=read();s[x[i]]++;s[y[i]]++;}

rpt(i,1,n) s[i]+=s[i-1];rpd(i,n,1) s[i]=s[i-1];

rpt(i,1,n-1) t[++s[x[i]]]=y[i],t[++s[y[i]]]=x[i];

source=rand()%n+1;

f[source][0]=d[source]=0;q[source]=v[source];dfs(source);

rpt(i,1,n) modify(l[i],q[i]^q[p[l[i]-1]]);

rpt(j,1,18) rpt(i,1,n) if(f[i][j-1]) f[i][j]=f[f[i][j-1]][j-1];

k=read();

while(k--){

scanf("%s",ss);xx=read();yy=read();

if(ss[0]=='C'){modify(l[xx],v[xx]^yy);if(r[xx]<n)modify(r[xx]+1,v[xx]^yy);v[xx]=yy;}

else if(sum(l[xx])^sum(l[yy])^v[lca(xx,yy)])puts("Yes"); else puts("No");

}

fclose(stdin);

fclose(stdout);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: