bzoj2819 nim (樹上帶修改查詢路徑異或和)
阿新 • • 發佈:2018-10-02
pos 得到 can mes upd ets name new 完成 (n)的時間內完成一次查詢。
題目簡述:
給定一棵樹,n個節點,每個節點表示一個石子堆。有m個操作,操作分兩種,第一種修改節點中石子數量,第二種查詢兩個節點路徑上的所有石子堆玩nim遊戲,是否必勝。
數據範圍:n,m<=500000,石子堆數量<=int_max
分析:
首先需要知道,nim遊戲的必勝局面是石子堆的異或和不為0。基於以下性質,可以證明。
1.石子全部取完為必敗局面,其異或和為0.
2.任意異或和不為0的局面,存在一種取法,能夠轉化為異或和為0的局面。
3.任意異或和為0的局面,無論如何取,都將轉化為異或和不為0的局面。
於是原問題轉換為求路徑上的石子堆異或和。
方法一:使用鏈剖求lca,並利用線段樹或樹狀數組維護重鏈上的異或和,則可以在log2
方法二:先做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\n":"No\n"); } } 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\n":"No\n"); } } return 0; }
bzoj2819 nim (樹上帶修改查詢路徑異或和)