bzoj2819 nim (树上带修改查询路径异或和)
Posted hefenghhhh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj2819 nim (树上带修改查询路径异或和)相关的知识,希望对你有一定的参考价值。
题目简述:
给定一棵树,n个节点,每个节点表示一个石子堆。有m个操作,操作分两种,第一种修改节点中石子数量,第二种查询两个节点路径上的所有石子堆玩nim游戏,是否必胜。
数据范围:n,m<=500000,石子堆数量<=int_max
分析:
首先需要知道,nim游戏的必胜局面是石子堆的异或和不为0。基于以下性质,可以证明。
1.石子全部取完为必败局面,其异或和为0.
2.任意异或和不为0的局面,存在一种取法,能够转化为异或和为0的局面。
3.任意异或和为0的局面,无论如何取,都将转化为异或和不为0的局面。
于是原问题转换为求路径上的石子堆异或和。
方法一:使用链剖求lca,并利用线段树或树状数组维护重链上的异或和,则可以在log2(n)的时间内完成一次查询。
方法二:先做dfs处理,得到一个dfs序列,序列中每个节点出现两次,入栈一次,出栈一次,在任意子树在dfs序列中均为一段连续区间。
对dfs序列使用树状数组维护前缀异或和。
使用倍增处理任意两点的lca。
查询路径(u,v)的异或和,即为getsum(st[u])^getsum(st[v])^stone[lca[u,v]]
其中st[u]表示节点u在dfs序列中第一次出现的位置。
修改节点u的石子数,即可update(st[u],stone[u]),update(ed[u]+1,stone[u]),update(st[u],newvalue),update(ed[u]+1,newvalue)
时间复杂度为O(MlogN).但常数略大。似乎比第一种方法要慢些。
由于担心爆栈,故写了手动栈。
方法一:链剖求lca,使用了bfs。发现一次bfs即可,代替了原来的两次dfs写法。
#include <iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; #define MAXN 500005 int fir[MAXN],nxt[MAXN<<1],edge[MAXN<<1],fa[MAXN],son[MAXN],depth[MAXN],pos[MAXN],maxson[MAXN],sz[MAXN],top[MAXN]; #define lowbit(x) (x&-x) int stone[MAXN],xorarr[MAXN]; int que[MAXN<<1],head,tail,ecnt; bool vis[MAXN]; int n,k,q; char opt[3]; void getnum(int &t) { char c; t=0; int flag=1; while(c=getchar(),(c>‘9‘||c<‘0‘))if(c==‘-‘)flag=-1; while(c>=‘0‘&&c<=‘9‘){t=t*10+c-48;c=getchar();} t*=flag; } void adde(int a,int b) { ecnt++; edge[ecnt]=b,nxt[ecnt]=fir[a],fir[a]=ecnt; ecnt++; edge[ecnt]=a,nxt[ecnt]=fir[b],fir[b]=ecnt; } void insert(int id,int num) { while(id<=n) { xorarr[id]^=num; id+=lowbit(id); } } void bfs(int x) { que[tail++]=x; depth[x]=1; fa[x]=0; while(head<tail) { int t=que[head++]; for(int i=fir[t];i;i=nxt[i]) { int v=edge[i]; if(v!=fa[t]) { depth[v]=depth[t]+1; fa[v]=t; que[tail++]=v; } } } for(int i=tail-1;i>=0;i--) { sz[que[i]]++; if(i==0)break; sz[fa[que[i]]]+=sz[que[i]]; if(sz[que[i]]>maxson[fa[que[i]]]) maxson[fa[que[i]]]=sz[que[i]],son[fa[que[i]]]=que[i]; } int k=0; for(int i=0;i<tail;i++) { int t=que[i]; if(vis[t]==1)continue; top[t]=t; while(t) { vis[t]=1; //xorarr[++k]=t; insert(++k,stone[t]); pos[t]=k; t=son[t]; if(t)top[t]=top[fa[t]]; } } } int getsum(int id) { int sum=0; while(id>0) { sum^=xorarr[id]; id-=lowbit(id); } return sum; } int query(int u,int v) { int tmp=0; while(top[v]!=top[u]) { if(depth[top[v]]>depth[top[u]])swap(u,v); tmp^=getsum(pos[top[u]]-1); tmp^=getsum(pos[u]); u=fa[top[u]]; } if(depth[v]>depth[u])swap(u,v); tmp^=getsum(pos[v]-1); tmp^=getsum(pos[u]); return tmp; } void change(int u,int value) { insert(pos[u],stone[u]); insert(pos[u],value); stone[u]=value; } int main() { freopen("nim.in","r",stdin); freopen("nim.out","w",stdout); int a,b; getnum(n); for(int i=1;i<=n;i++) getnum(stone[i]); for(int i=1;i<n;i++) getnum(a),getnum(b),adde(a,b); bfs(1); getnum(q); for(int i=1;i<=q;i++) { scanf("%s",opt); //getnum(a),getnum(b); scanf("%d %d",&a,&b); if(opt[0]==‘C‘) { change(a,b); } else if(opt[0]==‘Q‘) { printf((query(a,b)!=0)?"Yes ":"No "); } } return 0; }
//方法二:先dfs序,倍增求lca,dfs采用了非递归写法。
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #define MAXN 500005 using namespace std; #define lowbit(x) (x&-x) int fir[MAXN],edge[MAXN<<1],nxt[MAXN<<1],ecnt,pos,n,q; char opt[3]; int f[MAXN][20]; int depth[MAXN]; int que[MAXN],head,tail; int btree[MAXN<<1]; int stone[MAXN]; int stk1[MAXN],stk2[MAXN],top,st[MAXN],ed[MAXN]; bool vis[MAXN]; void getnum(int &t) { t=0; char c; int flag=1; while(c=getchar(),c<‘0‘||c>‘9‘)if(c==‘-‘)flag=-1; while(c<=‘9‘&&c>=‘0‘){t=t*10+c-48;c=getchar();} t*=flag; } void adde(int a,int b) { ecnt++; edge[ecnt]=b,nxt[ecnt]=fir[a],fir[a]=ecnt; ecnt++; edge[ecnt]=a,nxt[ecnt]=fir[b],fir[b]=ecnt; } void dfs(int s) { stk1[++top]=s; stk2[top]=fir[s]; st[s]=++pos; depth[s]=1; vis[s]=1; for(int i=0;i<19;i++) f[s][i]=0; while(top) { int i=stk2[top]; if(i) {int v=edge[i]; stk2[top]=nxt[i]; if(!vis[v]) { vis[v]=1; depth[v]=depth[stk1[top]]+1; f[v][0]=stk1[top]; for(int i=1;i<19;i++) f[v][i]=f[f[v][i-1]][i-1]; stk1[++top]=v; st[v]=++pos; stk2[top]=fir[v]; } } else ed[stk1[top--]]=++pos; } } void update(int x,int value) { while(x<=2*n) { btree[x]^=value; x+=lowbit(x); } } int getsum(int x) { int sum=0; while(x>0) { sum^=btree[x]; x-=lowbit(x); } return sum; } int getlca(int a,int b) { if(depth[a]<depth[b])swap(a,b); int diff=depth[a]-depth[b]; for(int i=0;i<20;i++) { if((diff>>i)&1) a=f[a][i]; } if(a==b)return a; for(int i=19;i>=0;i--) { if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i]; } return f[a][0]; } void change(int pos,int value) { update(st[pos],stone[pos]); update(ed[pos],stone[pos]); update(st[pos],value); update(ed[pos],value); stone[pos]=value; } int query(int a,int b) { int sum=0; int c=getlca(a,b); sum=getsum(st[a]); sum^=getsum(st[b]); sum^=stone[c]; return sum; } int main() { int a,b; getnum(n); for(int i=1;i<=n;i++) getnum(stone[i]); for(int i=1;i<n;i++) getnum(a),getnum(b),adde(a,b); dfs(1); for(int i=1;i<=n;i++) { update(st[i],stone[i]); update(ed[i],stone[i]); } getnum(q); for(int i=1;i<=q;i++) { scanf("%s",opt); getnum(a),getnum(b); if(opt[0]==‘C‘) change(a,b); else { printf(query(a,b)?"Yes ":"No "); } } return 0; }
以上是关于bzoj2819 nim (树上带修改查询路径异或和)的主要内容,如果未能解决你的问题,请参考以下文章