树形DP+FWT--hdu5909Tree Cutting

传送门

暴力就是 O ( n 3 ) O(n^3) O(n3)的树形 d p dp dp,设 f [ u ] [ i ] f[u][i] f[u][i] u u u为根的子树,异或和为 i i i的方案数,那么转移就是
f [ u ] [ i ] = ∑ v ∈ s o n u ( f [ v ] [ i ] + ∑ j = 0 m − 1 f [ v ] [ j ] × f [ u ] [ i ⊕ j ] ) f[u][i]=\sum_{v\in son_u}(f[v][i]+\sum_{j=0}^{m-1}f[v][j]\times f[u][i\oplus j]) f[u][i]=vsonu(f[v][i]+j=0m1f[v][j]×f[u][ij])
注意这里的转移可能会有重叠所以要用一个临时数组记下来,后面可以用 F W T FWT FWT优化

还有想说 h d u hdu hdu简直有毒, C + + C++ C++ T T T G + + G++ G++就能 A A A,而且好像还不太能写快读???

#include
#include
#include
#include
#include
#include
#define N 1100
using namespace std;

const int mod=1e9+7;

int t,n,m,val[N],a[N],b[N];
int f[N][N],inv2,ans[N];
vector<int> mp[N];

inline void FWT(int F[],int type){
	for(int mid=1;mid<m;mid<<=1)
		for(int r=mid<<1,j=0;j<m;j+=r)
			for(int k=0;k<mid;k++){
				int x=F[j+k],y=F[j+mid+k];
				F[j+k]=(x+y)%mod,F[j+mid+k]=(x-y+mod)%mod;
				if(type==-1) F[j+k]=1LL*inv2*F[j+k]%mod,F[j+mid+k]=1LL*inv2*F[j+mid+k]%mod;
			}
}

inline void solve(int a[],int b[]){
	FWT(a,1); FWT(b,1);
	for(int i=0;i<m;i++) a[i]=1LL*a[i]*b[i]%mod;
	FWT(a,-1);
}

void DP(int u,int fa){
	f[u][val[u]]=1;
	for(int i=0;i<mp[u].size();i++){
		int v=mp[u][i]; if(v==fa) continue;
		DP(v,u);
		for(int j=0;j<m;j++) a[j]=f[u][j];
		solve(f[u],f[v]);
		for(int j=0;j<m;j++) f[u][j]=(f[u][j]+a[j])%mod;
	}
	for(int i=0;i<m;i++) ans[i]=(ans[i]+f[u][i])%mod;
}

int main(){
	scanf("%d",&t); inv2=(mod+1)>>1; int x,y;
	while(t--){
		scanf("%d%d",&n,&m);
		memset(f,0,sizeof f); memset(ans,0,sizeof ans);
		for(int i=1;i<=n;i++) scanf("%d",&val[i]),mp[i].clear();
		for(int i=1;i<n;i++){
			scanf("%d%d",&x,&y);
			mp[x].push_back(y); mp[y].push_back(x);
		}
		DP(1,0);
		for(int i=0;i<m-1;i++) printf("%d ",ans[i]); printf("%d\n",ans[m-1]);
	}
	return 0;
}

你可能感兴趣的:(树形dp,FWT)