题目描述:
给定一棵无根树,统计所有子树的异或和的个数.
题目描述:
定义
dp[i][j]
d
p
[
i
]
[
j
]
以i为根的子树中异或和为j的方案数
初始化
dp[i][val[i]]=1
d
p
[
i
]
[
v
a
l
[
i
]
]
=
1
枚举子节点
dp[i][j⊕k]=dp[i][j⊕k]+dp[i][j⊕k]∗dp[x][k]
d
p
[
i
]
[
j
⊕
k
]
=
d
p
[
i
]
[
j
⊕
k
]
+
d
p
[
i
]
[
j
⊕
k
]
∗
d
p
[
x
]
[
k
]
⊕
⊕
表示异或
这样的转移是
m2
m
2
的
观察到上面的式子可以用FWT进行快速统计 转移变为
mlogm
m
l
o
g
m
总复杂度
nmlogm
n
m
l
o
g
m
题目链接:
Ac 代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
const int maxm=3000;
const int mod=1e9+7,inv=5e8+4;
int head[maxm],to[maxm<<1],net[maxm<<1],cnt;
int dp[maxm][maxm],tmp[maxm],ans[maxm],val[maxm];
inline void addedge(int u,int v)
{
cnt++;
to[cnt]=v,net[cnt]=head[u],head[u]=cnt;
}
inline void FWT(int *a,int n,int f)
{
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=(i<<1))
for(int k=0;k<i;k++)
{
int x=a[j+k],y=a[i+j+k];
if(~f) a[j+k]=(x+y)%mod,a[i+j+k]=(x-y+mod)%mod;
else a[j+k]=(1ll*(x+y)*inv)%mod,a[i+j+k]=(1ll*(x-y+mod)*inv)%mod;
}
}
inline void slove(int *a,int *b,int n)
{
FWT(a,n,1),FWT(b,n,1);
for(int i=0;i<n;i++) a[i]=(1ll*a[i]*b[i])%mod;
FWT(a,n,-1);
}
void dfs(int now,int fa,int n)
{
dp[now][val[now]]=1;
for(int i=head[now];i;i=net[i])
if(to[i]!=fa)
{
dfs(to[i],now,n);
for(int j=0;j<n;j++) tmp[j]=dp[now][j];
slove(dp[now],dp[to[i]],n);
for(int j=0;j<n;j++) dp[now][j]=(dp[now][j]+tmp[j])%mod;
}
for(int i=0;i<n;i++) ans[i]=(ans[i]+dp[now][i])%mod;
}
int n,k;
inline void work()
{
cnt=0;
memset(head,0,sizeof(head));
memset(dp,0,sizeof(dp));
memset(ans,0,sizeof(ans));
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&val[i]);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs(1,0,k);
for(int i=0;i<k-1;i++) printf("%d ",ans[i]);
printf("%d\n",ans[k-1]);
}
int main()
{
int t;
scanf("%d",&t);
while(t--) work();
return 0;
}