1. 程式人生 > >bzoj4134 ljw和lzr的hack比賽 trie樹合併

bzoj4134 ljw和lzr的hack比賽 trie樹合併

題目分析

首先,我們刪掉所有被Hack的點,剩下的點的父親,為原樹上它的第一個沒有被Hack的祖先。則產生了一個森林。

那麼對於這個遊戲局面,sg值為每棵樹的sg值的異或和。

現在考慮一棵樹的sg怎麼算。記g(x)g(x)為在這棵樹中選一個節點xx,將xx與其祖先全部刪除,剩下的子樹們的sg異或和。那麼該樹中所有g(x)g(x)的mex就是該樹的sg。

我們發現,假設我們現在在處理子樹xx,處理子樹yyyyxx的父親)的時候,子樹xx中所有節點的g(x)g(x)都要異或yy除了xx以外的兒子們的sg值。於是考慮用trie樹來維護g(x)g(x)

x),異或就只要打標記就可以了。

然後做trie樹合併獲得yy的trie樹,我們知道trie樹合併的複雜度是O(nlogn)O(n\log n)的。

再插入所有yy的所有兒子異或和,即g(y)g(y)

求出整棵trie樹的mex,方法是記錄trie樹中以每個節點為根的子樹是否是滿的,如果是當前節點的左子樹是滿的,則在右子樹中尋找這個mex值,否則在左子樹中找。(往左走的邊是0,往右走的邊是1)

為了獲得答案,我們還要記錄每個g(x)g(x)值對應的xx,這個只要對於trie樹上的每個葉子節點開一個連結串列即可。

程式碼

#include<bits/stdc++.h>
using namespace std; #define RI register int int read() { int q=0;char ch=' '; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar(); return q; } const int N=100005,mxd=25; int c[N],h[N],ne[N<<1],to[N<<1],fa[N],sg[N],st[N]; int bin[35],s[N*
25][2],full[N*25],rt[N],tag[N*25],fir[N*25],nxt[N],tail[N*25]; int n,tot,ans,SZ,top,debug; void puttag(int x,int num,int d) { if(num&bin[d]) swap(s[x][0],s[x][1]); tag[x]^=num; } void pd(int x,int d) { if(s[x][0]) puttag(s[x][0],tag[x],d-1); if(s[x][1]) puttag(s[x][1],tag[x],d-1); tag[x]=0; } void up(int x) {full[x]=full[s[x][0]]&full[s[x][1]];} int merge(int x,int y,int d) { if(!x||!y) return x|y; if(d<0) { nxt[tail[x]]=fir[y],tail[x]=tail[y]; return x; } if(tag[x]) pd(x,d); if(tag[y]) pd(y,d); s[x][0]=merge(s[x][0],s[y][0],d-1); s[x][1]=merge(s[x][1],s[y][1],d-1); up(x);return x; } void ins(int &x,int d,int num,int id) { if(!x) x=++SZ; if(d<0) { full[x]=1; if(!fir[x]) fir[x]=tail[x]=id; else nxt[tail[x]]=id,tail[x]=id; return; } if(tag[x]) pd(x,d); if(num&bin[d]) ins(s[x][1],d-1,num,id); else ins(s[x][0],d-1,num,id); up(x); } int mex(int x,int d) { if(d<0) return 0; if(tag[x]) pd(x,d); if(!full[s[x][0]]) return mex(s[x][0],d-1); else return bin[d]+mex(s[x][1],d-1); } void work(int x) { int sum=0; for(RI i=h[x];i;i=ne[i]) work(to[i]),sum^=sg[to[i]]; for(RI i=h[x];i;i=ne[i]) puttag(rt[to[i]],sum^sg[to[i]],mxd); for(RI i=h[x];i;i=ne[i]) rt[x]=merge(rt[x],rt[to[i]],mxd); ins(rt[x],mxd,sum,x),sg[x]=mex(rt[x],mxd); } void getans(int x,int d,int num) { if(d<0) { for(RI i=fir[x];i;i=nxt[i]) st[++top]=i; return; } if(tag[x]) pd(x,d); if(num&bin[d]) getans(s[x][1],d-1,num); else getans(s[x][0],d-1,num); } void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;} void dfs(int x,int las,int OvO) { fa[x]=OvO; for(RI i=h[x];i;i=ne[i]) { if(to[i]==las) continue; if(!c[x]) dfs(to[i],x,x); else dfs(to[i],x,OvO); } } int main() { int x,y; n=read(); bin[0]=1;for(RI i=1;i<=mxd;++i) bin[i]=bin[i-1]<<1; for(RI i=1;i<=n;++i) c[i]=read(); for(RI i=1;i<n;++i) x=read(),y=read(),add(x,y),add(y,x); dfs(1,0,0); tot=0;for(RI i=1;i<=n;++i) h[i]=0; for(RI i=1;i<=n;++i) if(!c[i]&&fa[i]) add(fa[i],i); for(RI i=1;i<=n;++i) if(!c[i]&&!fa[i]) work(i),ans^=sg[i]; if(!ans) puts("-1"); else { for(RI i=1;i<=n;++i) if(!c[i]&&!fa[i]) getans(rt[i],mxd,ans^sg[i]); sort(st+1,st+1+top); for(RI i=1;i<=top;++i) printf("%d\n",st[i]); } return 0; }