[hdu5909]Tree Cutting——动态规划+FWT
题目大意:
给定一棵树,求有多少个联通块满足异或和=k,对于每一个k求答案。
思路:
设\(dp_{i,j}\)表示联通块深度最小的点为i时,异或和为j时有多少个满足条件。
从儿子转移,FWT优化即可。
/*=======================================
* Author : ylsoi
* Time : 2019.2.10
* Problem : hdu5909
* E-mail : ylsoi@foxmail.com
* ====================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("hdu5909.in","r",stdin);
freopen("hdu5909.out","w",stdout);
}
template<typename T>void read(T &_){
_=0; T f=1; char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
_*=f;
}
const int maxn=1000+10;
const int maxm=2048+10;
const int mod=1e9+7;
const int inv=(mod+1)/2;
int T,n,m,w[maxn];
vector<int>G[maxn];
ll dp[maxn][maxm],ans[maxm];
void fwt(ll *A,int ty){
for(int len=1;len<m;len<<=1)
for(int L=0;L<m;L+=len<<1)
REP(i,L,L+len-1){
ll x=A[i],y=A[i+len];
A[i]=(x+y)%mod;
A[i+len]=(x-y)%mod;
if(ty==-1)A[i]=A[i]*inv%mod,A[i+len]=A[i+len]*inv%mod;
}
}
void dfs(int u,int fh){
REP(i,0,m)dp[u][i]=0;
dp[u][w[u]]=1;
fwt(dp[u],1);
REP(i,0,G[u].size()-1){
int v=G[u][i];
if(v==fh)continue;
dfs(v,u);
REP(j,0,m-1)dp[u][j]=dp[u][j]*dp[v][j]%mod;
}
fwt(dp[u],-1),++dp[u][0],fwt(dp[u],1);
}
int main(){
File();
read(T);
while(T--){
read(n),read(m);
REP(i,1,n)read(w[i]);
int u,v;
REP(i,1,n-1){
read(u),read(v);
G[u].pb(v);
G[v].pb(u);
}
dfs(1,0);
REP(i,1,n)fwt(dp[i],-1);
REP(i,0,m-1){
ans[i]=0;
REP(j,1,n)ans[i]=(ans[i]+dp[j][i])%mod;
if(!i)ans[i]=((ans[i]-n)%mod+mod)%mod;
printf("%lld%c",ans[i],i==m-1 ? '\n' : ' ');
}
REP(i,1,n)G[i].clear();
}
return 0;
}