Skip to content

树形背包 $ O(N \sqrt N ) $ ​

cpp
/*
题意:
求将树分解成k和k+1块的方案数
*/
#include <ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;
const int RANDOM = chrono::high_resolution_clock::now().time_since_epoch().count();
struct chash {
    int operator()(int x) const { return x ^ RANDOM; }
};
typedef gp_hash_table<int, int,chash> hash_t;
void solve(){
	int n,m;
	cin>>n>>m;
	vector<int> adj[n+1],siz(n+1);
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		adj[u].push_back(v);
		adj[v].push_back(u);
	}
	if(m<=2*sqrt(n)){
		vector<vector<int>> f(n+1,vector<int>(m+2));
		vector<int> g(m+2);
		function<void(int,int)> dfs1=[&](int u,int fa){
			f[u][1]=1;
			siz[u]=1;
			for(auto v:adj[u]){
				if(v==fa)continue;
				dfs1(v,u);
				for(int i=0;i<=m+1;i++)g[i]=0;
				for(int j=min(m+1,siz[u]);j>=0;j--){
					for(int k=min(m+1-j,siz[v]);k>=0;k--){
						(g[j+k]+=f[u][j]*f[v][k]%mod)%=mod;
					}
				}
				for(int i=0;i<=m+1;i++)f[u][i]=g[i];
				siz[u]+=siz[v];
			}
			(f[u][0]+=f[u][m]+f[u][m+1])%=mod;
		};
		dfs1(1,0);
		cout<<f[1][0]<<"\n";
		return;
	}
	hash_t f[n+1];
	function<void(int,int)> dfs2=[&](int u,int fa){
		f[u][1]=1;
		hash_t g;
		for(auto v:adj[u]){
			if(v==fa)continue;
			dfs2(v,u);
			for(auto &[j,val1]:f[u]){
				for(auto &[k,val2]:f[v]){
					if(j+k>m+1)continue;
					(g[j+k]+=val1*val2%mod)%=mod;
				}
			}
			f[u].clear();
			for(auto [i,val]:g)f[u][i]=val;
			g.clear();
		}
		(f[u][0]+=f[u][m]+f[u][m+1])%=mod;
		vector<int> tmp;
		for(auto &[i,val]:f[u]){
			if(!val)tmp.push_back(i);
		}
		for(auto &it:tmp)f[u].erase(it);
	};
	dfs2(1,0);
	cout<<f[1][0]<<"\n";
}