bzoj4134 ljw和lzr的hack比赛 trie树合并

版权声明:这篇文章的作者是个蒟蒻,没有转载价值,如果要转说一下好了 https://blog.csdn.net/litble/article/details/82902701

题目分析

首先,我们删掉所有被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;
}

猜你喜欢

转载自blog.csdn.net/litble/article/details/82902701