agc050F
- 首先关键在于mod 2的性质使得对于一些对称的方案可以不考虑。
- 如果有相邻两次操作没有交点,那么显然可以交换,其他不变得到相同的结果。具体来说不妨假设操作偶数条边,操作序列两个两个一组,如果没有交就与交换之后的抵消,因此每一组边都相交在可能被统计入答案。
- 再考虑对于NAND操作,如果连续操作两次会发生什么。发现除了000,010,101对称的以外,都相当于是一个点连续吃掉两个点而自己不变,而上面的特殊情况又可以交换顺序使得方案为0,因此问题转化为了每一次选择一个点连续吃掉两个点。
- 不妨考虑一个根节点,如果有一次操作与根节点无关那么取反之后就抵消了,因此问题又转化为选择一个根节点,每一次吃掉两个点,最后剩下根节点,从叶子考虑发现划分唯一,直接计算即可。
- 对于奇数条边的可以枚举第一次操作哪条边。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 305
#define ll long long
using namespace std;
int n,i,j,k,E[maxn][2],col[maxn];
int tot,c[maxn];
int em,e[maxn*2],nx[maxn*2],ls[maxn];
void insert(int x,int y){
em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
}
int ans,C[maxn][maxn],sz[maxn],s;
void dfs(int x,int p){
sz[x]=0; int tp=0;
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p){
int y=e[i]; dfs(y,x);
if (sz[y]&1){if (tp) s=0; else tp=1;}
s&=C[(sz[x]+sz[y])/2][sz[y]/2],sz[x]+=sz[y];
}
sz[x]++;
}
void doit(int xx){
for(int rt=1;rt<=n;rt++) if (rt!=xx&&c[rt])
s=1,dfs(rt,0),ans^=s;
}
int main(){
freopen("ceshi.in","r",stdin);
scanf("%d",&n);
for(i=1;i<n;i++) scanf("%d%d",&E[i][0],&E[i][1]);
for(i=1;i<=n;i++) scanf("%d",&col[i]);
for(C[0][0]=1,i=1;i<=n;i++) for(C[i][0]=1,j=1;j<=i;j++)
C[i][j]=C[i-1][j]^C[i-1][j-1];
if (n&1){
for(i=1;i<n;i++) insert(E[i][0],E[i][1]);
for(i=1;i<=n;i++) c[i]=col[i];
doit(0);
} else {
for(int now=1;now<n;now++){
em=0,memset(ls,0,sizeof(ls));
int xx=E[now][0],yy=E[now][1];
for(i=1;i<=n;i++) if (i==xx) c[i]=!(col[xx]&col[yy]);
else if (i!=yy) c[i]=col[i];
for(i=1;i<n;i++) if (i!=now)
insert((E[i][0]==yy)?xx:E[i][0],(E[i][1]==yy)?xx:E[i][1]);
doit(yy);
}
}
printf("%d\n",ans);
}