题解:发现问题的本质,即堆的个数
动态规划一下
f[i]表示前i个元素形成的堆的个数
第i个元素为根,左右子树又是两个堆
注意:逆元存在条件
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long Lint; const int maxn=1000009; int n,mm; Lint f[maxn]; int h[maxn]; Lint g[maxn]; Lint Ksm(Lint a,int p){ Lint ret=1; for(;p;p>>=1,a=a*a%mm){ if(p&1)ret=ret*a%mm; } return ret; } Lint Inv(Lint x){ return Ksm(x,mm-2); } Lint C(int n,int m){ int tmp=h[n]-h[m]-h[n-m]; if(tmp)return 0; return f[n]*Inv(f[m])%mm*Inv(f[n-m])%mm; } int t=0; int main(){ scanf("%d%d",&n,&mm); f[0]=1;h[0]=0; for(int i=1;i<=n;++i){ int x=i;h[i]=h[i-1]; while(x%mm==0){ x/=mm;++h[i]; } f[i]=f[i-1]*x%mm; } g[0]=g[1]=1; for(int i=2;i<=n;++i){ while((1<<(t+1))-1<=i)++t; int res=i-((1<<t)-1); if(res>=(1<<(t-1))){ g[i]=C(i-1,(1<<t)-1)*g[(1<<t)-1]%mm*g[i-(1<<t)]%mm; }else{ g[i]=C(i-1,(1<<(t-1))+res-1)*g[(1<<(t-1))+res-1]%mm*g[(1<<(t-1))-1]%mm; } } cout<<g[n]<<endl; }