题意:给定一棵带点权的树,每次询问用一条路径上的点玩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. How To Create an IE-Only Stylesheet

    https://css-tricks.com/how-to-create-an-ie-only-stylesheet/ https://css-tricks.com/snippets/css/css- ...

  2. Linux 退格键不回显

    在程序使用system("stty erase ^H");可以实现在输入状态下,按退格键删除字符,不回显. 调用tcsetattr修改linux基本输入的控制字符定义 //Linu ...

  3. 基于SignalR的小型IM系统

    这个IM系统真是太轻量级了,提供的功能如下: 1.聊天内容美化 2.用户上下线提示 3.心跳包检测机制 4.加入用户可群聊 下面来一步一步的讲解具体的制作方法. 开篇准备工作 首先,巧妇难为无米之炊, ...

  4. iptables/Netfilter 学习

    开始学iptables,因为它是和路由器技术紧密结合在一起的. iptables的命令看起来眼花缭乱,随便找两个: iptables -A FORWARD -p tcp -s -d -j ACCEPT ...

  5. STM32 控制红外线收发

    买了一块STM32的板子,这次需要将IR的code移植到STM32上面,因为STM32成本比树莓派低得多,所以 一些简单的外设挂在STM32上就行了. 我买的板子的型号是STM32F103C8T6,价 ...

  6. web安全——代理(nginx)

    场景 过滤非正常用户使用的http请求. 限制正常用户使用的范围(下载速度.访问频率等). 通过架构规划来提升安全. 能自动解决http请求问题. 解决方案 代理自身的安全 千万不要使用root启动! ...

  7. 关于Node.js的httpClieint请求报错ECONNRESET的原因和解决措施

    背景说明 最近在工作项目中有下面一个场景: 使用Node.js的express框架实现了一个文件系统服务器端,其中有个API用于客户端上传文件.客户端使用Node.js的HttpClient来调用服务 ...

  8. 备忘:powerbroker运行一个命令

    pbrun su<space>-<space><taget user name> example: pbrun su - pmsdev

  9. js前端分页

    转载:http://www.cnblogs.com/lyzg/p/5791011.html http://www.cnblogs.com/m-m-g-y0416/p/5601903.html

  10. 63-w 简明笔记

    显示关于系统用户的信息 w [options] [username] w用于显示当前登录系统的用户的名字以及他们的终端设备编号.登录时间.正在运行的命令和其他一些信息 参数 username 限定仅显 ...