[luoguRemoteJudge11414][trie][博弈论]SPOJ:Combat on a tree

link

考虑算一个点的sg值,如果这个点是黑点,那就把子树的能到达的状态求出来放在trie树上,然后在trie树上跑一遍就好,如果是白点则要额外加入这个点能到达的状态
然后合并子树就可以用01trie合并(类似线段树合并)
我们还要计算一个点能否被选,记录up[x]表示删除x到根节点的所有点后,剩下的不为x子树的树的sg函数的异或值,则根据博弈论的那一套理论,我们把up[x]异或上sum[x](表示x所有子树sg值的异或和)后如果为0那么表示选了这个点后是必败态,那就可以选这个点了
up值可以一遍dfs求出

Code:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=1e5+5,M=16;
int vis[N<<1],nxt[N<<1],head[N],tot=0;
inline void add(int x,int y){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
namespace OItrie{
	struct trie{
		int son[2],ful,xr;
	}tr[N*20];
	int cnt;
	#define ls(k) tr[k].son[0]
	#define rs(k) tr[k].son[1]
	inline void pushup(int x){tr[x].ful=tr[ls(x)].ful && tr[rs(x)].ful;}
	inline void pushxr(int k,int x,int i){
		if(x&(1<<i)) swap(ls(k),rs(k));
		tr[k].xr^=x;
	}
	inline void pushdown(int k,int i){if(i && tr[k].xr){pushxr(ls(k),tr[k].xr,i-1);pushxr(rs(k),tr[k].xr,i-1);tr[k].xr=0;}}
	void ins(int &k,int dep){
		k=++cnt;
		if(dep<0) {tr[k].ful=1;return;}
		ins(ls(k),dep-1);
	}
	int merge(int x,int y,int dep){
		if(!x || !y) return x|y;
		if(dep<0) return x;
		pushdown(x,dep);pushdown(y,dep);
		ls(x)=merge(ls(x),ls(y),dep-1);
		rs(x)=merge(rs(x),rs(y),dep-1);
		pushup(x);return x;
	}
	int ask(int k,int dep){
		if(!k || dep<0) return 0;
		pushdown(k,dep);
		return tr[ls(k)].ful?(1<<dep)+ask(rs(k),dep-1):ask(ls(k),dep-1);
	}
}
using namespace OItrie;
int sum[N],col[N],rt[N],sg[N];
void dfs1(int v,int fa){
	if(!col[v]) ins(rt[v],M);
	int summ=0;
	for(int i=head[v];i;i=nxt[i]){
		int y=vis[i];
		if(y==fa) continue;
		dfs1(y,v);
		summ^=sg[y];
		pushxr(rt[y],sg[y],M);
		rt[v]=merge(rt[v],rt[y],M);
	}
	if(summ) pushxr(rt[v],summ,M);
	sum[v]=summ;sg[v]=ask(rt[v],M);
}
int up[N];
void dfs2(int v,int fa){
	up[v]=up[fa]^sum[fa]^sg[v];
	for(int i=head[v];i;i=nxt[i]){
		int y=vis[i];
		if(y==fa) continue;
		dfs2(y,v);
	}
}
int main(){
	int n=read();
	for(int i=1;i<=n;i++) col[i]=read();
	for(int x,y,i=1;i<n;i++){x=read();y=read();add(x,y);add(y,x);}
	dfs1(1,0);sum[0]=sg[1];dfs2(1,0);
	int flag=0;
	for(int i=1;i<=n;i++) if(!col[i] && !(sum[i]^up[i])) flag=1,cout<<i<<"\n";
	if(!flag) puts("-1");
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值