bzoj2819 Nim
题意:给定一棵带点权的树,每次询问用一条路径上的点玩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的更多相关文章
- BZOJ2819: Nim 树链剖分
Description 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游 ...
- BZOJ2819 Nim 【dfn序 + lca + 博弈论】
题目 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的. ...
- bzoj2819: Nim(博弈+树剖)
2819: Nim 题目:传送门 题解: 很久之前学博弈的时候看过的一道水题,其实算不上博弈吧... 直接套上一个裸的树剖啊,把路径上的点值全都xor(xor满足结合率所以就不管那么多随便搞啦) do ...
- BZOJ2819 Nim(DFS序)
题目:单点修改.树链查询. 可以直接用树链剖分做.. 修改是O(QlogN),查询是O(QlogNlogN),Q=N=500000: 听说会超时.. 这题也可以用DFS序来做. 先不看修改,单单查询: ...
- 【手动开栈】【dfs序】【树状数组】【Tarjan】bzoj2819 Nim
考虑树状数组区间修改(只对其子树的答案有影响)点查询,每个点记录的是它到根路径上的权值异或和. 答案时query(L)^query(R)^a[lca]. 这种方法在支持区间加法.减法的树上询问的时候可 ...
- 【bzoj2819】 Nim
www.lydsy.com/JudgeOnline/problem.php?id=2819 (题目链接) 题意 动态树上路径异或和. Solution Nim取石子游戏的sg值就是每堆石子的异或和,所 ...
- 【BZOJ2819】Nim 树状数组+LCA
[BZOJ2819]Nim Description 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可 ...
- 【bzoj2819】Nim
Description 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游 ...
- 【bzoj2819】Nim(dfs序+树状数组/线段树)
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2819 首先根据SG定理,可得若每堆石子数量的异或值为0,则后手必胜,反之先手必胜.于是 ...
随机推荐
- C#.NET 大型通用信息化系统集成快速开发平台 4.0 版本 - 省市区数据权限的实现效果
折腾了2-3周,终于把全国网点数据权限,省.市.县数据规范化,查询权限规范化,基础数据规范化的思路理清楚了, 今天应该是一个里程碑式的一天 省市区数据规范化后 1:网点的基础数据可以更加严谨规范化. ...
- 我的职业生涯总结---班门弄斧之我们该怎样从零开始学习.NET
标题说的很清楚了,这篇文章纯属班门弄斧,大神可随意喷.我只是结合自己4年不到的学习与使用.NET的水平. 首先说下这篇博客的背景吧.前两天有个我的读者加我微信,然后就有了下面这样的对话, 可能有些人第 ...
- c/s 自动升级(WebService)
首先声明,本人文笔不好,大家见笑,欢迎高手吐槽. 做c/s开发肯定会遇到的就是自动升级功能,而这实现方式是非常多. 本文使用 webservice的方式来提供升级服务 首先准备服务 为了方便我们专门用 ...
- sql server死锁神器
参考文章: http://blogs.msdn.com/b/sqlserverfaq/archive/2013/04/27/an-in-depth-look-at-sql-server-memory- ...
- Python时间性能测量
主要有以下三种方式: 一,CPU时间 time.clock() 测量CPU时间,比较精准,通过比较程序运行前后的CPU时间差,得出程序运行的CPU时间. 二, 时钟时间 time.time() 测量时 ...
- We Know What @You #Tag: Does the Dual Role Affect Hashtag Adoption-20160520
分析类的论文 1.Information publication:www2012 author: Mei qiao zhu 2.What 微博中的hashtag既可以表示谈论的内容,又可以代表一个群体 ...
- Java Little Knowledge
1.Constructor running order of Base class and Derived class This is Alibaba's audition problem. clas ...
- 61-umask 简明笔记
设定在创建文件时的权限掩码 umask [mask] 参数 mask可以是3位八进制数或者是如同在chmod中使用的符号值,mask指定不允许的权限(文件的实际权限是777减去umask值) 如果没有 ...
- 如何调试js文件
来源于:http://stackoverflow.com/questions/988363/how-can-i-debug-my-javascript-code http://stackoverflo ...
- you-get中文说明
来源于:https://github.com/soimort/you-get/wiki/%E4%B8%AD%E6%96%87%E8%AF%B4%E6%98%8E You-Get 乃一小小哒命令行程序, ...