bzoj4134 ljw和lzr的hack比賽 trie樹合併
阿新 • • 發佈:2018-12-12
題目分析
首先,我們刪掉所有被Hack的點,剩下的點的父親,為原樹上它的第一個沒有被Hack的祖先。則產生了一個森林。
那麼對於這個遊戲局面,sg值為每棵樹的sg值的異或和。
現在考慮一棵樹的sg怎麼算。記為在這棵樹中選一個節點,將與其祖先全部刪除,剩下的子樹們的sg異或和。那麼該樹中所有的mex就是該樹的sg。
我們發現,假設我們現在在處理子樹,處理子樹(為的父親)的時候,子樹中所有節點的都要異或除了以外的兒子們的sg值。於是考慮用trie樹來維護,異或就只要打標記就可以了。
然後做trie樹合併獲得的trie樹,我們知道trie樹合併的複雜度是的。
再插入所有的所有兒子異或和,即。
求出整棵trie樹的mex,方法是記錄trie樹中以每個節點為根的子樹是否是滿的,如果是當前節點的左子樹是滿的,則在右子樹中尋找這個mex值,否則在左子樹中找。(往左走的邊是0,往右走的邊是1)
為了獲得答案,我們還要記錄每個值對應的,這個只要對於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;
}