题意:给定一棵带点权的树,每次询问用一条路径上的点玩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. 分享我对领域驱动设计(DDD)的学习成果

    本文内容提要: 1. 领域驱动设计之领域模型 2. 为什么建立一个领域模型是重要的 3. 领域通用语言(Ubiquitous Language) 4.将领域模型转换为代码实现的最佳实践 5. 领域建模 ...

  2. &12 二叉搜索树

    #1,定义 二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的 ...

  3. HDU2389-Rain on your Parade-二分图匹配-ISAP

    裸二分图匹配 /*--------------------------------------------------------------------------------------*/ #i ...

  4. K-means算法及文本聚类实践

    K-Means是常用的聚类算法,与其他聚类算法相比,其时间复杂度低,聚类的效果也还不错,这里简单介绍一下k-means算法,下图是一个手写体数据集聚类的结果. 基本思想 k-means算法需要事先指定 ...

  5. 在使用EF Code First开发时,遇到的“关系”问题,以及解决方法

    Entity Framework Code First 简称 EF CF也行,就是在开发的时候,以代码先行的原则,开发人员无需考虑 数据库端的一些问题(开发过程中基本不需要在数据库管理器上操作) 言归 ...

  6. 与Python Falling In Love_Python跨台阶(面向对象)

    第二课会介绍Python中的一些变量的使用.列表.元组.字典等一些详细内容...篇幅会比较多...因此在整理中... 先跳过第二课...直接来第三课..Python中面向对象的学习以及与mysql数据 ...

  7. C语言字符串与数字相互转换

    在C/C++语言中没有专门的字符串变量,通常用字符数组来存放字符串.字符串是以“\0”作为结束符.C/C++提供了丰富的字符串处理函数,下面列出了几个最常用的函数. ● 字符串输出函数puts. ● ...

  8. JavaScript学习笔记-随滚轮匀速滑动的浮动广告窗动画

    <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...

  9. JSON字符串——后台解析系列

    以前我们都是讲JSON字符串获取后,在前台进行展示.今天小编就交给大家后台解析展示数据的方法.非常方便,就以下代码: JObject obj = JObject.Parse(data); string ...

  10. Go语言interface详解

    interface Go语言里面设计最精妙的应该算interface,它让面向对象,内容组织实现非常的方便,当你看完这一章,你就会被interface的巧妙设计所折服. 什么是interface 简单 ...