[HDU 5909] Tree Cutting

一、題目

點此看題

題目描述

給你一棵nn個節點的樹,每個節點都有一個小於mm的權值,定義一棵子樹的權值爲所有節點的異或和,問權值爲0..m10..m−1的所有子樹的個數。(這裏子樹的意思是聯通子圖)

二、解法

dp[i][j]dp[i][j]爲第ii個點異或值爲jj的方案數,枚舉子節點,轉移如下:
dp[u][k]=ij=kdp[u][i]×dp[v][j]dp[u][k]=\sum_{i\oplus j=k}dp[u][i]\times dp[v][j]這顯然是fwt\text{fwt}的形式,可以用fwt\text{fwt}優化,我們初始化把每個節點的權值那一位賦成11,回溯時需要增加異或和00的增加11(父親可以不選這個兒子),我們把每個點的dpdp求和最後輸出就行了(因爲每個點的dpdp值必然選了當前點而且只考慮子樹)

#include <cstdio>
#include <cstring>
const int M = 1030;
const int MOD = 1e9+7;
int read()
{
    int num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
int T,n,m,tot,f[M],dp[M][M],ans[M],inv2=(MOD+1)/2;
struct edge
{
	int v,next;
}e[2*M];
void fwt_xor(int *a,int n,int op)
{
	for(int i=1;i<n;i<<=1)
		for(int p=i<<1,j=0;j<n;j+=p)
			for(int k=0;k<i;k++)
			{
				int x=a[j+k],y=a[i+j+k];
				a[j+k]=(x+y)%MOD;
				a[i+j+k]=(x+MOD-y)%MOD;
				if(op==-1)
				{
					a[j+k]=1ll*a[j+k]*inv2%MOD;
					a[i+j+k]=1ll*a[i+j+k]*inv2%MOD;
				}
			}
}
void dfs(int u,int fa)
{
	fwt_xor(dp[u],m,1);
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa) continue;
		dfs(v,u);
		fwt_xor(dp[v],m,1);
		for(int j=0;j<m;j++)
			dp[u][j]=1ll*dp[u][j]*dp[v][j]%MOD;
	}
	fwt_xor(dp[u],m,-1);
	for(int i=0;i<m;i++)
		ans[i]=(ans[i]+dp[u][i])%MOD;
	dp[u][0]++;
}
signed main()
{
	T=read();
	while(T--)
	{
		memset(dp,0,sizeof dp);
		memset(ans,0,sizeof ans);
		n=read();m=read();
		for(int i=1;i<=n;i++)
		{
			tot=f[i]=0;
			dp[i][read()]=1;
		}
		for(int i=1;i<n;i++)
		{
			int u=read(),v=read();
			e[++tot]=edge{v,f[u]},f[u]=tot;
			e[++tot]=edge{u,f[v]},f[v]=tot;
		}
		dfs(1,0);
		printf("%d",ans[0]);
		for(int i=1;i<m;i++)
			printf(" %d",ans[i]);
		puts("");
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章