[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 ? '
' : ' ');
		}
		REP(i,1,n)G[i].clear();
	}
	return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/10359368.html