Jzoj3805 小X的二叉堆计数

题意:给你n个不同的数问你能构成多少个不同的二叉堆

显然不能枚举,我们考虑用递推

我们令f[i]表示以i为根的二叉堆有多少种

令l,r为i的左右儿子,令size[i]为以i为根的堆的大小

那么显然,f[i]=f[l]*f[r]*C(size[i]-1,size[l]),因为n个数互不相同,所以没有重复

相当于是1~size[i]这几个数填进这个堆中,那么显然根只能填最大那个,让后在剩下的数选择size[l]个即可

因为堆有两种,大根堆和小根堆,所以答案要乘2

#include<stdio.h>
#define M 1000000007
#define L long long
#define N 5000010
L inv[N],js[N],f[N];
int n,sz[N];
inline L C(int n,int m){
	return js[n]*inv[n-m]%M*inv[m]%M;
}
inline L pow(L x,int k){
	L S=1;
	for(;k;x=x*x%M,k>>=1)
		if(k&1) S=S*x%M;
	return S;
}
int main(){
	scanf("%d",&n);
	*js=*inv=1;
	for(int i=1;i<=n;++i) {
		js[i]=js[i-1]*i%M;
		inv[i]=pow(js[i],M-2);
	}
	for(int i=n;i;--i){
		sz[i>>1]+=++sz[i];
		L l=(i<<1)>n?1:f[i<<1];
		L r=(i<<1|1)>n?1:f[i<<1|1];
		L p=(i<<1)>n?0:sz[i<<1];
		f[i]=l*r%M*C(sz[i]-1,p)%M;
		if(!f[i]) ++f[i];
	}
	if(n==1) { return 0&puts("1"); }
	else printf("%d",(f[1]<<1)%M);
}


posted @ 2017-10-02 11:26  扩展的灰(Extended_Ash)  阅读(129)  评论(0编辑  收藏  举报