一、題目
題目描述
給你一棵個節點的樹,每個節點都有一個小於的權值,定義一棵子樹的權值爲所有節點的異或和,問權值爲的所有子樹的個數。(這裏子樹的意思是聯通子圖)
二、解法
設爲第個點異或值爲的方案數,枚舉子節點,轉移如下:
這顯然是的形式,可以用優化,我們初始化把每個節點的權值那一位賦成,回溯時需要增加異或和的增加(父親可以不選這個兒子),我們把每個點的求和最後輸出就行了(因爲每個點的值必然選了當前點而且只考慮子樹)
#include <cstdio>
#include <cstring>
const int M = 1030;
const int MOD = 1e9+7;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int T,n,m,tot,f[M],dp[M][M],ans[M],inv2=(MOD+1)/2;
struct edge
{
int v,next;
}e[2*M];
void fwt_xor(int *a,int n,int op)
{
for(int i=1;i<n;i<<=1)
for(int p=i<<1,j=0;j<n;j+=p)
for(int k=0;k<i;k++)
{
int x=a[j+k],y=a[i+j+k];
a[j+k]=(x+y)%MOD;
a[i+j+k]=(x+MOD-y)%MOD;
if(op==-1)
{
a[j+k]=1ll*a[j+k]*inv2%MOD;
a[i+j+k]=1ll*a[i+j+k]*inv2%MOD;
}
}
}
void dfs(int u,int fa)
{
fwt_xor(dp[u],m,1);
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
dfs(v,u);
fwt_xor(dp[v],m,1);
for(int j=0;j<m;j++)
dp[u][j]=1ll*dp[u][j]*dp[v][j]%MOD;
}
fwt_xor(dp[u],m,-1);
for(int i=0;i<m;i++)
ans[i]=(ans[i]+dp[u][i])%MOD;
dp[u][0]++;
}
signed main()
{
T=read();
while(T--)
{
memset(dp,0,sizeof dp);
memset(ans,0,sizeof ans);
n=read();m=read();
for(int i=1;i<=n;i++)
{
tot=f[i]=0;
dp[i][read()]=1;
}
for(int i=1;i<n;i++)
{
int u=read(),v=read();
e[++tot]=edge{v,f[u]},f[u]=tot;
e[++tot]=edge{u,f[v]},f[v]=tot;
}
dfs(1,0);
printf("%d",ans[0]);
for(int i=1;i<m;i++)
printf(" %d",ans[i]);
puts("");
}
}