【HDU5909】Tree Cutting FWT
令 $f[x][j]$ 表示以 $x$ 为根的子树,选出连通块的异或值为 $j$ 的方案数.
然后有 $f[x][j]=f[x][j]+\sum_{i\oplus k=j} f[x][i] \times f[son][k]$.
其中,$\oplus$ 为异或符号.
求解这个东西显然可以用 $FWT$ 来加速.
有两种方式,第一个是当遍历 $x$ 的儿子的时候分别于每一个儿子都做一次 $FWT$,但是这个会比较慢.
一个更好的方式是我们知道若干个多项式相乘不必一一相乘,而是统一进行 $FWT$,然后最后做一次 $IFWT$ 就行.
但是这里有一个地方需要注意:$f[x][0]$ 必须要 +1,因为当 $x$ 的父亲与 $x$ 结合时有可能不选 $x$.
#include <cstdio> #include <cstring> #include <string> #include <vector> #include <cctype> #include <algorithm> #define N 1306 #define RG register #define ll long long #define mod 1000000007 #define setIO(s) freopen(s".in","r",stdin) using namespace std; inline int read() { RG int x=0,t=1;RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } int lim,inv,n; int f[N][N],ans[N]; vector<int>G[N]; int qpow(int x,int y) { int tmp=1; for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) tmp=(ll)tmp*x%mod; return tmp; } void FWT(int *a,int opt) { int i,j,k; for(i=1;i<lim;i<<=1) { for(j=0;j<lim;j+=i<<1) { for(k=0;k<i;++k) { int tmp=a[j+k]; a[j+k]=(ll)(a[j+k]+a[j+k+i])%mod; a[j+k+i]=(ll)(tmp-a[j+k+i]+mod)%mod; if(opt==-1) { a[j+k]=(ll)inv*a[j+k]%mod; a[j+k+i]=(ll)inv*a[j+k+i]%mod; } } } } } void dfs(int u,int ff) { FWT(f[u],1); for(int i=0;i<G[u].size();++i) { int y=G[u][i]; if(y==ff) continue; dfs(y,u); for(int j=0;j<lim;++j) f[u][j]=(ll)f[u][j]*f[y][j]%mod; } FWT(f[u],-1),f[u][0]=(f[u][0]+1)%mod,FWT(f[u],1); G[u].clear(); } int main() { // setIO("input"); inv=qpow(2,mod-2); int i,j,T; T=read(); while(T--) { // scanf("%d%d",&n,&lim); n=read(),lim=read(); memset(f,0,sizeof(f)),memset(ans,0,sizeof(ans)); for(i=1;i<=n;++i) { ++f[i][read()]; } for(i=1;i<n;++i) { int x=read(),y=read(); G[x].push_back(y); G[y].push_back(x); } dfs(1,0); for(i=1;i<=n;++i) FWT(f[i],-1); for(i=1;i<=n;++i) f[i][0]=(f[i][0]-1+mod)%mod; for(i=1;i<=n;++i) for(j=0;j<lim;++j) ans[j]=(ll)(ans[j]+f[i][j])%mod; for(i=0;i<lim-1;++i) printf("%d ",ans[i]); printf("%d\n",ans[lim-1]); } return 0; }