其他分享
首页 > 其他分享> > CF724F Uniformly Branched Trees

CF724F Uniformly Branched Trees

作者:互联网

tag:重心,dp,组合计数


晕呼呼地计数...

题意

求 \(n\) 个点的不同的树的个数(同构视为一种,无标号),使得每个点的度数为 \(1\) 或 \(d\)。

\(n\le1000, 2\le d\le10\)


题解

无标号树同构问题一般想到找重心,把重心作为根,这里先假设重心唯一(\(n\) 为奇数)。

设一个树 \(dp\),\(f_{i,j,k}\) 表示当前子树一共有 \(i\) 个节点,当前根有 \(j\) 个儿子,每个儿子的子树大小都不超过 \(k\)。那么答案就是 \(f_{n,d,\left\lfloor\frac n2\right\rfloor}\)。


首先若所有子树都小于 \(k\),递归到 \(f_{i,j,k-1}\)。

第二种情况枚举 \(p\) 个子树大小为 \(k\),递归到 \(f_{i-pk,j-p,k-1}\),转移系数为 \(\binom{f_{k,d-1,k-1}+p-1}p\)。可以把每个子树理解为一个球,方案理解为一个盒子,因为子树之间没有顺序,所以问题对应为“将相同的球放入不同的盒子里,可以为空”的方案数。


然后考虑 \(n\) 为偶数。这时可能会有 \(2\) 个重心,会多算一些方案。比如重心 \(1\) 选择方案 \(a\),重心 \(2\) 选择方案 \(b\);和重心 \(1\) 选择方案 \(b\),重心 \(2\) 选择方案 \(a\),构成的是同一棵树。但是用之前的方法计算时,会把一个重心当作另一个重心的儿子,所以上述的两种方案会被视为两种不同的方案。所以还要减去 \(\binom{f_{\frac n2,d-1,\frac n2-1}}2\)。


注意一下特判 \(n\le2\)。

#include<bits/stdc++.h>
using namespace std;
 
template<typename T>
inline void Read(T &n){
    char ch; bool flag=false;
    while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
    for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
    if(flag)n=-n;
}

enum{
	MAXN = 1005,
	D = 15
};

int n, d, MOD, f[D][MAXN][MAXN];

inline int dec(int a, int b){
	a -= b;
	if(a<0) a += MOD;
	return a;
}

inline int ksm(int base, int k=MOD-2){
	int res=1;
	while(k){
		if(k&1)
			res = 1ll*res*base%MOD;
		base = 1ll*base*base%MOD;
		k >>= 1;
	}
	return res;
}

int inv[D];
inline void prework(int n){
	for(register int i=1; i<=n; i++) inv[i] = ksm(i);
}

inline int cn2(int n){return 1ll*n*(n-1)/2%MOD;}

int dp(int n, int d, int k){
	if(n==1 and !d) return 1;
	if(k==1) return d+1==n;
	if(~f[d][n][k]) return f[d][n][k];
	int &res = f[d][n][k] = 0;
	res = dp(n,d,k-1);
	int tmp = dp(k,::d-1,k-1), kkk = tmp;
	for(register int i=1; i*k<n and i<=d; i++)
		res = (res+1ll*dp(n-i*k,d-i,k-1)*kkk)%MOD,
		kkk = 1ll*kkk*(tmp+i)%MOD*inv[i+1]%MOD;
	return res;
}

int main(){
	Read(n); Read(d); Read(MOD);
	memset(f,-1,sizeof f);
	prework(d);
	if(n<=2) return puts("1"), 0;
	if(n&1) printf("%d\n",dp(n,d,n/2));
	else printf("%d\n",dec(dp(n,d,n/2),cn2(dp(n/2,d-1,n/2-1))));
	return 0;
}

标签:CF724F,方案,ch,frac,重心,int,Uniformly,子树,Branched
来源: https://www.cnblogs.com/oisdoaiu/p/14934095.html