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

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

題目分析

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

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

現在考慮一棵樹的sg怎麼算。記 g ( x ) g(x)

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

我們發現,假設我們現在在處理子樹 x x ,處理子樹 y

y y y x x 的父親)的時候,子樹 x x 中所有節點的 g ( x ) g(x) 都要異或 y y 除了 x x 以外的兒子們的sg值。於是考慮用trie樹來維護 g ( x ) g(x) ,異或就只要打標記就可以了。

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

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

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

為了獲得答案,我們還要記錄每個 g ( x ) g(x) 值對應的 x x ,這個只要對於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;
}