题意
给一棵树,根节点为1,每个节点有黑白两种颜色。每次操作可以选择一个白点,然后将其到根节点的路径染黑,不能操作者算输。问先手必胜的第一步操作有哪些。
n<=100000
分析
恩感觉自己对sg函数还不是那么的熟练。
这题我们可以先将这棵树的黑点去掉,然后建一个森林。
然后设g[x]表示我选择操作x后其所在子树所能到达的状态,也就等于将x到根的路径去掉后森林的sg值异或和。sg[x]表示x为根的子树所代表的sg函数值。
显然sg[x]等于x所在子树的g值(包括x自己)去个mex。
sg和g求好之后,设l为森林的sg值异或和,那么对于每棵树,设其根为root,那么这棵树内的g值为l^sg[root]的节点均为答案。
现在考虑如何求sg和g值。
我们可以用递归的方式来求,先设g[x]表示删除x到当前点的路径后所能到达的状态,sg[x]不变。
那么我们可以先递归处理子树,然后要将每棵子树内节点的g值异或上除了该儿子外其他儿子的sg值异或和,然后再求个mex即可得到当前点的sg值。
怎么实现呢?
要求资瓷插入,合并,异或,求mex的操作,我们可以用字典树。
插入不多说。合并的话就跟线段树合并是一样的。异或的话可以考虑打标记,是1的话就交换左右儿子即可。求mex,只要每个节点维护一个bz[x]表示x所在的树是否是满二叉树即可。
恩细节比较多,调了一早上。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=100005;
const int maxd=30;
int cnt,last[N],fa[N],a[N],a1,op[N],bin[35],sz,bz[N*maxd],sg[N],root[N],n;
struct trie{int son[2],rev;}t[N*maxd];
vector<int> id[N*maxd];
struct edge{int to,next;}e[N*2];
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void dfs(int x,int y,int z)
{
fa[x]=z;
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==y) continue;
if (op[x]) dfs(e[i].to,x,z);
else dfs(e[i].to,x,x);
}
}
void pushdown(int d,int now)
{
if (now<0||!t[d].rev) return;
int l=t[d].rev;t[d].rev=0;
if (l&bin[now]) swap(t[d].son[0],t[d].son[1]);
t[t[d].son[0]].rev^=l;t[t[d].son[1]].rev^=l;
}
void ins(int &d,int now,int x,int y)
{
if (!d) d=++sz;
else pushdown(d,now);
if (now<0)
{
bz[d]=1;id[d].push_back(y);
return;
}
int b;
if (x&bin[now]) b=1;
else b=0;
ins(t[d].son[b],now-1,x,y);
bz[d]=bz[t[d].son[0]]&bz[t[d].son[1]];
}
void merge(int &d,int p,int now)
{
pushdown(d,now);pushdown(p,now);
if (!d||!p)
{
d=p+d;return;
}
if (now<0)
{
for (vector<int>::iterator it=id[p].begin();it!=id[p].end();it++) id[d].push_back(*it);
id[p].clear();
return;
}
merge(t[d].son[0],t[p].son[0],now-1);
merge(t[d].son[1],t[p].son[1],now-1);
}
int mex(int d,int now)
{
pushdown(d,now);
if (!t[d].son[0]) return 0;
if (!bz[t[d].son[0]]) return mex(t[d].son[0],now-1);
if (!t[d].son[1]) return 1<<now;
return mex(t[d].son[1],now-1)+(1<<now);
}
void solve(int x,int fa)
{
int w=0;
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==fa) continue;
solve(e[i].to,x);
w^=sg[e[i].to];
}
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==fa) continue;
int l=w^sg[e[i].to];
t[root[e[i].to]].rev^=l;
merge(root[x],root[e[i].to],maxd);
}
ins(root[x],maxd,w,x);
sg[x]=mex(root[x],maxd);
}
void find(int d,int now,int x)
{
if (!d) return;
pushdown(d,now);
if (now<0)
{
for (vector<int>::iterator it=id[d].begin();it!=id[d].end();it++) a[++a1]=*it;
return;
}
int b;
if (x&bin[now]) b=1;
else b=0;
find(t[d].son[b],now-1,x);
}
int main()
{
bin[0]=1;
for (int i=1;i<=maxd;i++) bin[i]=bin[i-1]*2;
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&op[i]);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
}
dfs(1,0,0);
memset(last,0,sizeof(last));cnt=0;
for (int i=1;i<=n;i++)
if (!op[i]&&fa[i]) addedge(fa[i],i);
int l=0;
for (int i=1;i<=n;i++)
if (!fa[i]&&!op[i])
{
solve(i,0);
l^=sg[i];
}
if (!l) printf("-1");
else
{
for (int i=1;i<=n;i++)
if (!fa[i]&&!op[i]) find(root[i],maxd,l^sg[i]);
sort(a+1,a+a1+1);
for (int i=1;i<=a1;i++) printf("%d\n",a[i]);
}
return 0;
}