【SPOJ COT3】Combat on a tree(SG函数)(01Trie合并)

传送门


题解:

很显然这是一个ICG。由于删掉某个点到根的链之后可能会形成很多连通块,也就是独立局面,感觉用局面的NP性并不是很好做,考虑SG函数。

如果一个局面是终止局面,SG值为0。

否则,计算一个局面的SG值有两种方式:

  1. 一个游戏局面的SG值是该局面所有直接后继的SG值的mex
  2. 如果该游戏局面由若干个互不干涉的子局面构成,该局面的SG值是所有子局面SG值的异或和。

考虑怎么统计答案,我们需要计算删掉某一个点到根的链之后的局面的SG值,显然这个局面由若干连通块构成,考虑用第二种方式,也就是说我们需要计算以每个点为根的子树的SG值。

而以一个点为根的子树的SG值,我们考虑用第一种方式,那么我们需要考虑删掉子树中某个点到该点后的路径能够达到的SG值由哪些。

首先,如果它是白点,可以单独删除,那么需要考虑所有子树的xor和。

然后,考虑延伸到其中某一棵子树,显然其他子树的SG值直接xor进来得到该子树中能够达到的SG值。

于是用01Trie可以非常方便地支持打xortag,合并子树和查询mex了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int N=1e5+7;

namespace Trie{
	cs int N=::N*30,K=20;
	int son[N][2],full[N],tot;
	int tag[N],d[N];
	
	inline void push_xor(int u,int v){if(!u)return ;
		tag[u]^=v;if(v>>d[u]&1)std::swap(son[u][0],son[u][1]);
	}
	inline void pushdown(int u){
		if(tag[u]){
			push_xor(son[u][0],tag[u]);
			push_xor(son[u][1],tag[u]);
			tag[u]=0;
		}
	}
	inline void ins(int &rt,int val,int de=K){
		if(!rt)d[rt=++tot]=de;if(full[rt])return ;
		pushdown(rt);if(!~de){full[rt]=true;return ;}
		ins(son[rt][val>>de&1],val,de-1);
		full[rt]=full[son[rt][0]]&&full[son[rt][1]];
	}
	inline int merge(int u,int v){
		if(!u||!v)return u|v;
		if(full[u])return u;
		if(full[v])return v;
		pushdown(u);pushdown(v);
		son[u][0]=merge(son[u][0],son[v][0]);
		son[u][1]=merge(son[u][1],son[v][1]);
		full[u]=full[son[u][0]]&&full[son[u][1]];
		return u;
	}
	inline int mex(int rt){
		if(!rt)return 0;
		int ans=0,cur=rt;
		for(int re i=K;~i;--i){
			if(!full[son[cur][0]])cur=son[cur][0];
			else ans|=1<<i,cur=son[cur][1];
		}
		return ans;
	}
}

int rt[N],col[N];
int el[N],nxt[N<<1],to[N<<1],ecnt;
inline void adde(int u,int v){
	nxt[++ecnt]=el[u],el[u]=ecnt,to[ecnt]=v;
	nxt[++ecnt]=el[v],el[v]=ecnt,to[ecnt]=u;
}

int nd[N],ct;

int sub[N];
void dfs1(int u,int p){
	int son=0;
	for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=p)dfs1(v,u),son^=sub[v];
	if(!col[u])Trie::ins(rt[u],son);
	for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=p){
		Trie::push_xor(rt[v],son^sub[v]);
		rt[u]=Trie::merge(rt[u],rt[v]);
	}
	sub[u]=Trie::mex(rt[u]);
}

void dfs2(int u,int p,int out){
	int son=0;
	for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=p)son^=sub[v];
	if(!col[u]&&(out^son)==0)nd[++ct]=u;
	for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=p)dfs2(v,u,out^son^sub[v]);
}

signed main(){
#ifdef zxyoi
	freopen("cot.in","r",stdin);
#endif
	int n=gi();
	for(int re i=1;i<=n;++i)col[i]=gi();
	for(int re i=1;i<n;++i)adde(gi(),gi());
	dfs1(1,0);dfs2(1,0,0);
	if(!ct)cout<<"-1\n";
	else {
		std::sort(nd+1,nd+ct+1);
		for(int re i=1;i<=ct;++i)cout<<nd[i]<<"\n";
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷的SPOJ需要注册一个SPOJ账号并进行绑定才能进行交题。您可以按照以下步骤进行注册: 1. 打开洛谷网站(https://www.luogu.com.cn/)并登录您的洛谷账号。 2. 在网站顶部导航栏中找到“题库”选项,将鼠标悬停在上面,然后选择“SPOJ”。 3. 在SPOJ页面上,您会看到一个提示,要求您注册SPOJ账号并进行绑定。点击提示中的链接,将会跳转到SPOJ注册页面。 4. 在SPOJ注册页面上,按照要求填写您的用户名、密码和邮箱等信息,并完成注册。 5. 注册完成后,返回洛谷网站,再次进入SPOJ页面。您会看到一个输入框,要求您输入刚刚注册的SPOJ用户名。输入用户名后,点击“绑定”按钮即可完成绑定。 现在您已经成功注册并绑定了SPOJ账号,可以开始在洛谷的SPOJ题库上刷题了。祝您顺利完成编程练习!\[1\]\[2\] #### 引用[.reference_title] - *1* *3* [(洛谷入门系列,适合洛谷新用户)洛谷功能全解](https://blog.csdn.net/rrc12345/article/details/122500057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [luogu p7492 序列](https://blog.csdn.net/zhu_yin233/article/details/122051384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值