[HDU5909]Tree Cutting
题意
一棵\(n\)个节点的树,每个点有一个小于\(m\)的权值\(v_i\)。定义一个连通块的权值是这个连通块内所有点的权值异或和。求权值为\(0,1...m-1\)的连通块各有多少。膜\(10^9+7\)。
\(n\le1000,m=2^k,k\le10\)
sol
很简单有一个\(O(nm^2)\)的\(dp\),设\(f[i][j]\)表示以\(i\)号点为根权值为\(j\)的连通块个数,有转移:
\[f'[u][i]=\sum_{j\otimes k=i}f[u][j]*f[v][k]$$其中$v$是$u$的一个儿子。
然后这个东西长得和集合卷积一模一样,所以可以$FWT$优化。
注意一个子树是可以不选的,所以$f[i][0]$在处理完$i$这棵子树后要$+1$方便后续处理。
时间复杂度$O(nm)$
# code
```cpp
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 2e3+5;
const int mod = 1e9+7;
int T,n,m,to[N],nxt[N],head[N],cnt,f[N][N],ans[N];
void init(){
memset(f,0,sizeof(f));
memset(head,0,sizeof(head));
memset(ans,0,sizeof(ans));
cnt=0;
}
void link(int u,int v){
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
void fwt(int *P,int opt){
for (int i=1;i<m;i<<=1)
for (int p=i<<1,j=0;j<m;j+=p)
for (int k=0;k<i;++k){
int x=P[j+k],y=P[j+k+i];
P[j+k]=1ll*opt*(x+y)%mod;
P[j+k+i]=1ll*opt*(x-y+mod)%mod;
}
}
void mul(int *a,int *b){
for (int i=0;i<m;++i) a[i]=1ll*a[i]*b[i]%mod;
}
void dfs(int u,int fa){
fwt(f[u],1);
for (int e=head[u];e;e=nxt[e])
if (to[e]!=fa) dfs(to[e],u),mul(f[u],f[to[e]]);
fwt(f[u],(mod+1)/2);(f[u][0]+=1)%=mod;fwt(f[u],1);
}
int main(){
T=gi();
while (T--){
n=gi();m=gi();init();
for (int i=1;i<=n;++i) ++f[i][gi()];
for (int i=1,u,v;i<n;++i)
u=gi(),v=gi(),link(u,v),link(v,u);
dfs(1,0);
for (int i=1;i<=n;++i){
fwt(f[i],(mod+1)/2);(f[i][0]+=mod-1)%=mod;
for (int j=0;j<m;++j) (ans[j]+=f[i][j])%=mod;
}
printf("%d",ans[0]);
for (int i=1;i<m;++i) printf(" %d",ans[i]);
puts("");
}
return 0;
}
```\]