题意:给定一棵带点权的树,每次询问用一条路径上的点玩Nim游戏先手是否必胜,支持单点修改。

Nim游戏:所有堆的数目异或起来不为0时先手必胜,否则必败.

所以就是单点修改+路径异或和查询. 树剖一发,因为异或满足区间减法所以可以树剖套树状数组。(还有人说可以套zkw线段树?)

Dzy大爷说了一种dfs序+树状数组的方法:

http://dzy493941464.is-programmer.com/posts/40428.html

既然它只修改点的话,影响到的只是它这棵子树。那么很容易就想到了dfs序。这个子树就是连续一段。

先维护每个点dfs开始时和结束时的时间戳。修改的时候先在它自己的开始、结束位置上xor它自己变成零,然后再修改。

(x,y)路径上的xor值=query(x的开始) xor query(y的开始) xor lca(x,y)的点权。很好想通。LCA就倍增算一下好了。

没了。

“很好想通”,表示蒟蒻脑子有坑想了好久才明白QAQ。

首先,在求树上点对距离的时候我们是用节点到根的距离减去lca到根的距离,比如u到v的距离等于dis[u]+dis[v]-2*dis[lca(u,v)]

这里把点i到根节点路径上点的异或和记作sum[i],i的点权记作w[i],那么u,v路径上的异或和就是sum[u]^sum[v]^w[lca(u,v)]

这么做的原因在于lca(u,v)到根节点路径上的点权值在sum[u]和sum[v]中都出现,异或一下就没了.但是lca(u,v)的点权应当在答案中出现,却也被消掉了,所以还要单独异或上。

Lca可以倍增一发,那么问题还剩下动态维护每个点到根节点路径上的异或和.转换思路,分别考虑每个点能够影响其他哪些点的sum值,对于每个点i将它能影响的所有点的sum值进异或上w[i]。这个操作可以高效地完成,因为每个点只会影响它所在子树内的点的sum值。而一棵子树内的点在dfs序中是连续的一段,所以我们只需要一个数据结构支持区间修改单点查询,那么就可以树状数组了。查询sum[u]的时候直接查询u的DFS序在树状数组中对应的位置即可。

注意会卡爆栈,所以需要用BFS求DFS序。

#include<cstdio>
const int maxn=;
struct edge{
int to,next;
}lst[maxn<<];int len=;
int first[maxn];
void addedge(int a,int b){
lst[len].to=b;
lst[len].next=first[a];
first[a]=len++;
}
int w[maxn],pos[maxn],prt[maxn],depth[maxn],hvy[maxn],top[maxn],sz[maxn];
int q[maxn],head,tail;
int c[maxn];
inline int lowbit(int x){
return x&(-x);
}
void add(int x,int w){
for(;x<maxn;x+=lowbit(x)){
c[x]^=w;
}
}
int sum(int x){
int ans=;
for(;x;x-=lowbit(x))ans^=c[x];
return ans;
}
void bfs1(){
head=tail=;
q[tail++]=;depth[]=;
while(head!=tail){
int x=q[head++];
sz[x]=;
for(int pt=first[x];pt;pt=lst[pt].next){
if(lst[pt].to==prt[x])continue;
prt[lst[pt].to]=x;
depth[lst[pt].to]=depth[x]+;
q[tail++]=lst[pt].to;
}
}
for(int i=tail-;i>=;--i){
int x=q[i];
sz[prt[x]]+=sz[x];
if(sz[x]>sz[hvy[prt[x]]])hvy[prt[x]]=x;
}
}
void bfs2(){
top[]=;pos[]=;
for(int i=;i<tail;++i){
int x=q[i];
add(pos[x],w[x]);
if(hvy[x]){
top[hvy[x]]=top[x];
pos[hvy[x]]=pos[x]+;
int cntsz=sz[hvy[x]];
for(int pt=first[x];pt;pt=lst[pt].next){
if(lst[pt].to==prt[x]||lst[pt].to==hvy[x])continue;
top[lst[pt].to]=lst[pt].to;
pos[lst[pt].to]=pos[x]+cntsz+;
cntsz+=sz[lst[pt].to];
}
}
}
}
inline void swap(int &a,int &b){
int tmp=a;a=b;b=tmp;
}
int query(int u,int v){
int ans=;
int t1=top[u],t2=top[v];
while(t1!=t2){
if(depth[t1]<depth[t2])swap(t1,t2),swap(u,v);
ans^=sum(pos[t1]-);
ans^=sum(pos[u]);
u=prt[t1];t1=top[u];
}
if(depth[u]>depth[v])swap(u,v);
ans^=sum(pos[u]-);ans^=sum(pos[v]);
return ans;
}
int main(){
int n;scanf("%d",&n);
for(int i=;i<=n;++i)scanf("%d",w+i);
int a,b;
for(int i=;i<n;++i){
scanf("%d%d",&a,&b);
addedge(a,b);addedge(b,a);
}
bfs1();
bfs2();
int m;scanf("%d",&m);
char buf[];
while(m--){
scanf("%s%d%d",buf,&a,&b);
if(buf[]=='Q'){
if(query(a,b)==)printf("No\n");
else printf("Yes\n");
}else{
add(pos[a],w[a]);
w[a]=b;
add(pos[a],w[a]);
}
}
return ;
}
#include<cstdio>
const int maxn=;
struct edge{
int to,next;
}lst[maxn<<];int len=;
int first[maxn];
void addedge(int a,int b){
lst[len].to=b;
lst[len].next=first[a];
first[a]=len++;
}
int w[maxn];
int c[maxn];
inline int lowbit(int x){
return x&(-x);
}
void add(int x,int w){
for(;x<maxn;x+=lowbit(x))c[x]^=w;
}
int query(int x){
int ans=;
for(;x;x-=lowbit(x))ans^=c[x];
return ans;
}
int q[maxn];
int dfn[maxn],sz[maxn],depth[maxn];
int p[maxn][];
void bfs(){//求dfs序和倍增的预处理都在这里了
int head=,tail=,x,cntsz;
q[tail++]=;depth[]=;
while(head!=tail){
x=q[head++];
for(int pt=first[x];pt;pt=lst[pt].next){
if(lst[pt].to==p[x][])continue;
p[lst[pt].to][]=x;
depth[lst[pt].to]=depth[x]+;
q[tail++]=lst[pt].to;
}
for(int j=;p[x][j];++j)p[x][j+]=p[p[x][j]][j];
}
for(int i=tail-;i>=;--i){
x=q[i];
sz[x]++;
sz[p[x][]]+=sz[x];
}
dfn[]=;
for(int i=;i<tail;++i){
x=q[i];cntsz=;
for(int pt=first[x];pt;pt=lst[pt].next){
if(lst[pt].to==p[x][])continue;
dfn[lst[pt].to]=dfn[x]+cntsz+;
cntsz+=sz[lst[pt].to];
}
}
}
inline void swap(int &a,int &b){
int tmp=a;a=b;b=tmp;
}
int lca(int u,int v){
if(depth[u]<depth[v]){
swap(u,v);
}
for(int j=;j>=;--j){
if(depth[p[u][j]]>=depth[v])u=p[u][j];
}
if(u==v)return u;
for(int j=;j>=;--j){
if(p[u][j]!=p[v][j]){
u=p[u][j];v=p[v][j];
}
}
return p[u][];
} int main(){
int n;scanf("%d",&n);
for(int i=;i<=n;++i)scanf("%d",w+i);
int a,b;
for(int i=;i<n;++i){
scanf("%d%d",&a,&b);
addedge(a,b);addedge(b,a);
}
bfs();
for(int i=;i<=n;++i){
add(dfn[i],w[i]);add(dfn[i]+sz[i],w[i]);
}
int q;scanf("%d",&q);
char buf[];
int tmp;
while(q--){
scanf("%s%d%d",buf,&a,&b);
if(buf[]=='Q'){
tmp=query(dfn[a])^query(dfn[b])^w[lca(a,b)];
if(tmp){
printf("Yes\n");
}else{
printf("No\n");
}
}else{
add(dfn[a],w[a]);add(dfn[a]+sz[a],w[a]);
w[a]=b;
add(dfn[a],w[a]);add(dfn[a]+sz[a],w[a]);
}
}
return ;
}

bzoj2819 Nim的更多相关文章

  1. BZOJ2819: Nim 树链剖分

    Description 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游 ...

  2. BZOJ2819 Nim 【dfn序 + lca + 博弈论】

    题目 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的. ...

  3. bzoj2819: Nim(博弈+树剖)

    2819: Nim 题目:传送门 题解: 很久之前学博弈的时候看过的一道水题,其实算不上博弈吧... 直接套上一个裸的树剖啊,把路径上的点值全都xor(xor满足结合率所以就不管那么多随便搞啦) do ...

  4. BZOJ2819 Nim(DFS序)

    题目:单点修改.树链查询. 可以直接用树链剖分做.. 修改是O(QlogN),查询是O(QlogNlogN),Q=N=500000: 听说会超时.. 这题也可以用DFS序来做. 先不看修改,单单查询: ...

  5. 【手动开栈】【dfs序】【树状数组】【Tarjan】bzoj2819 Nim

    考虑树状数组区间修改(只对其子树的答案有影响)点查询,每个点记录的是它到根路径上的权值异或和. 答案时query(L)^query(R)^a[lca]. 这种方法在支持区间加法.减法的树上询问的时候可 ...

  6. 【bzoj2819】 Nim

    www.lydsy.com/JudgeOnline/problem.php?id=2819 (题目链接) 题意 动态树上路径异或和. Solution Nim取石子游戏的sg值就是每堆石子的异或和,所 ...

  7. 【BZOJ2819】Nim 树状数组+LCA

    [BZOJ2819]Nim Description 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可 ...

  8. 【bzoj2819】Nim

    Description 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游 ...

  9. 【bzoj2819】Nim(dfs序+树状数组/线段树)

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2819 首先根据SG定理,可得若每堆石子数量的异或值为0,则后手必胜,反之先手必胜.于是 ...

随机推荐

  1. React Native中设计主题机制

    昨天和同事讨论组件隔离性的时候讨论到关于默认样式的问题:很多情况下我们希望能够把组件设计为通用的,然后在具体项目中给他们指定一些通用的样式,譬如:背景颜色.默认字体等等.这听起来在CSS下运作起来就很 ...

  2. mac里git项目删除.DS_Store文件

    用mac开发项目,每次提交文件时都生成修改文件的.DS_Store文件,提交时会不会觉得比较烦?别急,下面给出解决方案.我们需要用到.gitignore文件去配置Git目录中需要忽略的文件. .git ...

  3. centos6-honeyd安装&配置

    安装 需要装 libpcap libevent libdnet 等(!) 有些用的yum,有些下载的安装包手动安装 (wget tar configure make install 非常linux) ...

  4. 让mysql支持emoji表情

    一.问题及原因 APP产品想对Emoji进行支持,但发现mysql数据库无法写入表情.原因是我们的mysql数据库默认用的是utf8编码,utf8编码存储时用的是三个字节,但Emoji表情是4个字节, ...

  5. C#链接阿里云KVStore

    KVStore的简单介绍 阿里云KVStore兼容Redis.因为KVStore就相当于Redis的服务器端,我们代码只是当作客户端,链接上服务器端就行了,阿里云的KVStore详情文档见,https ...

  6. Retro 2013

    现在的team里每个迭代都会做一次retro,回顾这两周的情况,有哪些做得好的地方,有哪些做得不足的地方,并制定出一系列action,以期望在下一个迭代中解决这些问题.我觉得这种形式挺不错.因此今年的 ...

  7. PHP后台代码 及 iOS客户端--AF实现上传视频

    //视频转换为MP4 //转码操作... _hud.mode = MBProgressHUDModeIndeterminate; _hud.labelText = @"转码中..." ...

  8. Ueditor 上传图片 如何设置只显示 本地上传

    我这个是自问自答,其实很简单.只要按照以下方式修改就可以了. 找到image.html 将以下代码 <div id="tabHeads" class="tabhea ...

  9. 使用Servlet实现下载文件的功能

    在前台有一个下载链接,比如 <a href="DownLoadServlet">下载</a> <br/> 使用Servlet实现下载: impo ...

  10. HttpModule与HttpHandler详解

    ASP.NET对请求处理的过程:当请求一个*.aspx文件的时候,这个请求会被inetinfo.exe进程截获,它判断文件的后缀(aspx)之后,将这个请求转交给 ASPNET_ISAPI.dll,A ...