您的位置:首页 > 其它

[BZOJ2819]Nim-树状数组-dfs序

2017-08-24 01:15 323 查看

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

Yes

No

Yes

Yes

Yes

警告:本文作者因调试手工栈而神志不清,接下来可能会出现代码风格奇怪、语言逻辑混乱等特殊现象,特此告知。

要是没有这该死的手工栈啊啊啊啊啊啊啊啊啊啊

调了这么久全TM因为手写栈啊啊啊啊啊啊啊啊啊啊

(╯‵□′)╯︵┻━┻

思路:

根据Nim游戏性质,先手存在必胜策略当且仅当所有石子数异或和不为0。

所以咱要维护的是异或值。

那么因为它只修改单点点权,这样影响的对象为其子树内的所有点,这在dfs序上正好为连续的一段。

于是考虑(用手工栈!!!!!)获取dfs序,然后运用差分,在一个点的dfs序起点处异或其值,终点处再异或其值以取消其值,即修改了子树内的值。

考虑回答询问。要查询路径上的异或和,就是对树状数组查询两个点dfs序上的起点,得到的值互相异或再异或上这两个点lca的值。

然后考虑修改,根据异或性质,只需像插入时一样再插入一次,即可消除其影响。

最后,lca咱用的是(用手工栈!!!!!!)写的树链剖分……

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

inline int read()
{
int x=0;char ch=getchar();
while(ch<'0' || '9'<ch)ch=getchar();
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x;
}

const int N=500009;
const int M=1000009;

int n;
int q
,cur
,st
,ed
,l,r;
int w
,to[N<<1],nxt[N<<1],beg
,tot;
int fa
,top
,siz
,son
,dep
,val
;
int bit[M];

inline int lowbit(int x)
{
return x&(-x);
}

inline int query(int x)
{
int ret=0;
while(x)
{
ret^=bit[x];
x-=lowbit(x);
}
return ret;
}

inline void insert(int a)
{
int x=st[a];
while(x<M)
{
bit[x]^=val[a];
x+=lowbit(x);
}
x=ed[a];
while(x<M)
{
bit[x]^=val[a];
x+=lowbit(x);
}
}

inline void adde(int u,int v)
{
to[++tot]=v;
nxt[tot]=beg[u];
beg[u]=tot;
}

inline void add(int u,int v)
{
adde(u,v);adde(v,u);
}

inline void pre()
{
memcpy(cur,beg,sizeof(beg));
q[r=1]=1;
st[1]=l=1;
fa[1]=0;
dep[1]=1;

while(r)
{
int u=q[r],v,flag=0;

if(cur[u] && to[cur[u]]==fa[u])
{
flag=1;
cur[u]=nxt[cur[u]];
}
else if(cur[u] && (v=to[cur[u]])!=fa[u])
{
fa[v]=u;
dep[v]=dep[u]+1;
st[v]=++l;

cur[u]=nxt[cur[u]];
q[++r]=v;

flag=1;
}
if(!flag)
{
ed[u]=++l;
r--;
}
}

l=0;
q[r=1]=1;
siz[1]=1;
while(l<r)
{
int u=q[++l];
for(int i=beg[u],v;i;i=nxt[i])
if((v=to[i])!=fa[u])
{
q[++r]=v;
siz[v]=1;
}
}

for(int i=n;i;i--)
siz[fa[q[i]]]+=siz[q[i]];

for(int i=1;i<=n;i++)
if(!son[fa[i]] || siz[son[fa[i]]]<siz[i])
son[fa[i]]=i;

top[1]=top[0]=1;
for(int i=1;i<=n;i++)
{
if(son[fa[q[i]]]==q[i])
top[q[i]]=top[fa[q[i]]];
else
top[q[i]]=q[i];
}
}

inline int lca(int a,int b)
{
while(top[a]!=top[b])
{
if(dep[top[a]]<dep[top[b]])
swap(a,b);
a=fa[top[a]];
}
if(dep[a]<dep[b])
return a;
return b;
}

int main()
{
n=read();
for(int i=1;i<=n;i++)
val[i]=read();
for(int i=1;i<n;i++)
add(read(),read());

pre();
for(int i=1;i<=n;i++)
insert(i);

int q=read();
char ch[4];
while(q--)
{
scanf("%s",ch);
if(ch[0]=='Q')
{
int u=read();
int v=read();
if(query(st[u])^query(st[v])^val[lca(u,v)])
puts("Yes");
else
puts("No");
}
else
{
int u=read();
int v=read();
insert(u);
val[u]=v;
insert(u);
}
}

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