[BZOJ 2819] Nim
Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2819
Algorithm:
此题一眼看上去树剖,但事实上双log会TLE
那么此时就要用到异或运算最重要的性质:自反性
如果设定一个root,只要计算x和y到root的路径即可,并不要轻重链剖分的计算路径,而这正是因为在LCA(x,y)以上的路径计算了两次,抵消了
但要注意的是,LCA这个点也被计算了两次,因此要再异或LCA这个点的值
如果只要计算一个点x到root的路径,那么当修改时只要维护到root的路径上含x的点:即x的子树
当涉及子树的整体操作时,明显可以使用dfs+RMQ的方式区间维护
线段树等确实可以用,但实现更方便的树桩数组明显是更好的选择,这就又利用了异或的自反性
初始化:
Update(l[i],dat[i]),Update(r[i]+1,dat[i]);
修改:
Update(l[x],dat[x]);Update(r[x]+1,dat[x]); Update(l[x],y);Update(r[x]+1,y); dat[x]=y;
这样查询时只要Query(l[x])即可,充分利用了差分思想,其在异或运算中一样实用
Code:
#include <bits/stdc++.h> using namespace std; const int MAXN=5e5+10; int n,m,dat[MAXN],bit[2*MAXN],f[MAXN][20],bin[25],l[MAXN],r[MAXN],dep[MAXN],cnt; vector<int> G[MAXN]; inline int read() { char ch;int num,f=0; while(!isdigit(ch=getchar())) f|=(ch=='-'); num=ch-'0'; while(isdigit(ch=getchar())) num=num*10+ch-'0'; return f?-num:num; } void Update(int pos,int val) { while(pos<=n) { bit[pos]^=val; pos+=pos&(-pos); } } inline int Query(int pos) { int ret=0; while(pos) { ret^=bit[pos]; pos-=pos&(-pos); } return ret; } void dfs(int x) { for(int i=1;i<20;i++) if(dep[x]>=bin[i]) f[x][i]=f[f[x][i-1]][i-1]; else break; l[x]=++cnt; for(int i=0;i<G[x].size();i++) { int t=G[x][i]; if(t==f[x][0]) continue; dep[t]=dep[x]+1,f[t][0]=x,dfs(t); } r[x]=cnt; } int LCA(int x,int y) { if(dep[x]<dep[y]) swap(x,y); int t=dep[x]-dep[y]; for(int i=19;i>=0;i--) if(t & bin[i]) x=f[x][i]; for(int i=19;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; if(x==y) return x; return f[x][0]; } int main() { bin[0]=1;for(int i=1;i<=20;i++) bin[i]=bin[i-1]<<1; n=read(); for(int i=1;i<=n;i++) dat[i]=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); G[x].push_back(y);G[y].push_back(x); } dfs(1); for(int i=1;i<=n;i++) Update(l[i],dat[i]),Update(r[i]+1,dat[i]); m=read(); for(int i=1;i<=m;i++) { char op[2];int x,y; scanf("%s",&op);x=read();y=read(); if(op[0]=='Q') { int lca=LCA(x,y); int res=Query(l[x])^Query(l[y])^dat[lca]; //Core if(res) puts("Yes"); else puts("No"); } else { Update(l[x],dat[x]);Update(r[x]+1,dat[x]); //Update Update(l[x],y);Update(r[x]+1,y); dat[x]=y; } } return 0; }
Review:
1、出现异或时,考虑自反性与差分法
2、对子树整体操作,dfs处理出l[i],r[i],从而优化
3、1e5以上的数据都要读写优化(或puts)