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