「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
No
Yes
Yes
Yes
题解
从kuribohG的博客摘录的
树链查询可以变成对某些点到根的查询的叠加,这样就只需要解决查询从根发出的树链即可。如果我们询问的是从根到X节点树链上的值,那么只有X节点或X节点的祖先可能影响到X节点的答案。也就是说,一个点修改以后,只会影响它为根的子树内的节点。这样我们把这个节点所在的子树区间[l,r]修改一下即可。查询的时候,如果查询的是X点,那么返回X节点所在DFS序中的那个位置的数即可。这样我们可以用线段树区间修改,单点查询来解决这个问题。当然也可以用BIT解决。解决方法是:修改X节点的时候,把l那个点在序列中的权值修改,把r+1那个点在序列中的权值修改。查询的时候返回1~l序列中的节点权值之和。
然后在bzoj下其实是不容易爆栈的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int read() { int x=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();} return x*f; } int bin[20]; int n,m,cnt; int t[500005]; int ind,l[500005],r[500005],val[500005]; int last[500005],deep[500005],fa[500005][20]; struct edge{ int to,next; }e[1000005]; void insert(int u,int v) { e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt; e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt; } void modify(int x,int val) { for(int i=x;i<=n;i+=i&-i) t[i]^=val; } int query(int x) { int tmp=0; for(int i=x;i;i-=i&-i) tmp^=t[i]; return tmp; } void dfs(int x) { for(int i=1;i<=18;i++) if(deep[x]>=bin[i]) fa[x][i]=fa[fa[x][i-1]][i-1]; else break; l[x]=++ind; for(int i=last[x];i;i=e[i].next) if(e[i].to!=fa[x][0]) { deep[e[i].to]=deep[x]+1; fa[e[i].to][0]=x; dfs(e[i].to); } r[x]=ind; } int lca(int x,int y) { if(deep[x]<deep[y])swap(x,y); int t=deep[x]-deep[y]; for(int i=0;i<=18;i++) if(bin[i]&t)x=fa[x][i]; for(int i=18;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; if(x==y)return x; return fa[x][0]; } int main() { bin[0]=1;for(int i=1;i<20;i++)bin[i]=bin[i-1]<<1; n=read(); for(int i=1;i<=n;i++)val[i]=read(); for(int i=1;i<n;i++) { int u=read(),v=read(); insert(u,v); } dfs(1); for(int i=1;i<=n;i++) modify(l[i],val[i]),modify(r[i]+1,val[i]); m=read(); while(m--) { char ch[2]; scanf("%s",ch); int x=read(),y=read(); if(ch[0]=='Q') { int t=lca(x,y); int res=query(l[x])^query(l[y])^val[t]; if(res)puts("Yes"); else puts("No"); } else { modify(l[x],val[x]);modify(r[x]+1,val[x]); val[x]=y; modify(l[x],y);modify(r[x]+1,y); } } return 0; } |
ds
快速读入卖萌QAQ
快省选了orzorzorzorzorzorzorzorzorzorzorzorz
请问这题用lct会有什么问题吗?
不会吧
反正我交上去超时了。。。
差分都是卡线过的 LCT必然爆炸
orzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz