http://acm.hdu.edu.cn/showproblem.php?pid=5909
题目大意:⼀棵
N
N
N个节点的树,每个节点有⼀个权值
v
i
v_i
vi,⼦树的权值定义为子树内所有节点权值的异或和。问各个权值的子树分别有多少种?
思路:设
d
p
[
u
]
[
i
]
dp[u][i]
dp[u][i]表示以
u
u
u为根的且权值为
i
i
i的子树的个数,初始时
d
p
[
i
]
[
a
[
i
]
]
=
1
dp[i][a[i]]=1
dp[i][a[i]]=1,其余的为
0
0
0。考虑在
d
f
s
dfs
dfs的过程中,访问了子节点
v
v
v之后(设父节点为
u
u
u),
u
u
u对
v
v
v的贡献应该为:
v
a
l
[
i
]
=
∑
j
x
o
r
k
=
i
d
p
[
u
]
[
j
]
∗
d
p
[
v
]
[
k
]
val[i]=\sum_{j\ xor\ k\ =\ i}dp[u][j]*dp[v][k]
val[i]=j xor k = i∑dp[u][j]∗dp[v][k]
d
p
[
u
]
[
i
]
+
=
v
a
l
[
i
]
dp[u][i]+=val[i]
dp[u][i]+=val[i]
现在考虑怎么求
v
a
l
[
i
]
val[i]
val[i],暴力?
O
(
n
m
2
)
O(nm^2)
O(nm2)显然不可取,我们思考一下
F
W
T
FWT
FWT是用来干什么的:
和上面的式子对应一下是不是有点想法?没错,用
F
W
T
FWT
FWT加速这个求和过程就可以了,时间复杂度
O
(
n
∗
m
l
g
m
)
O(n*mlgm)
O(n∗mlgm),还是可以接受的~
#include<bits/stdc++.h>
using namespace std; //FWT
typedef long long ll;
const int maxn=1024+5;
const int MOD=1e9+7;
struct Edge
{
int to,nxt;
}edge[maxn];
int t,n,m,limit,tot;
int head[maxn];
ll a[maxn],b[maxn],dp[maxn][maxn];
inline void addedge(int u,int v)
{
edge[++tot].to=v,edge[tot].nxt=head[u],head[u]=tot;
}
inline void FWT_xor(ll *A,int inv)
{
ll x,y;
ll inv2=MOD+1>>1; //因为MOD是一个质数 所以(MOD+1)/2 就是2模MOD的乘法逆元
for(int mid=1;mid<limit;mid<<=1)
{
for(int i=0;i<limit;i+=mid<<1)
{
for(int j=0;j<mid;j++)
{
x=A[i+j],y=A[mid+i+j];
A[i+j]=(x+y)%MOD;
A[mid+i+j]=(x-y+MOD)%MOD;
if(inv==-1)
{
A[i+j]=A[i+j]*inv2%MOD; // 2模MOD的乘法逆元 如果题目不是在模意义下的 除2即可
A[mid+i+j]=A[mid+i+j]*inv2%MOD;
}
}
}
}
}
void dfs(int u,int fa)
{
int v;
for(int i=head[u];i;i=edge[i].nxt)
{
v=edge[i].to;
if(v!=fa)
{
dfs(v,u);
memcpy(a,dp[u],sizeof(dp[u]));
memcpy(b,dp[v],sizeof(dp[v]));
FWT_xor(a,1),FWT_xor(b,1);
for(int i=0;i<limit;i++)
a[i]=a[i]*b[i]%MOD;
FWT_xor(a,-1);
for(int i=0;i<limit;i++)
{
dp[u][i]+=a[i];
if(dp[u][i]>=MOD)
dp[u][i]%=MOD;
}
}
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
tot=0;
memset(dp,0,sizeof(dp));
scanf("%d %d",&n,&m);
limit=m;
int u,v;
for(int i=1;i<=n;i++)
scanf("%d",&v),dp[i][v]=1,head[i]=0;
for(int i=1;i<n;i++)
{
scanf("%d %d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs(1,0);
ll ans;
for(int i=0;i<m;i++)
{
ans=0;
for(int j=1;j<=n;j++)
{
ans+=dp[j][i];
if(ans>=MOD)
ans%=MOD;
}
printf("%lld",ans);
if(i!=m-1)
putchar(' ');
}
putchar('\n');
}
return 0;
}