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);
}