这什么毒瘤玩意儿……
我们通过题解区发现这个要求就是小根堆的性质,考虑枚举树的大小,显然最小的为树根,考虑左右两棵子树的答案相乘即可。((l),(r)分别表示左右子树的大小)
[f[i]={i-1 choose l}*f[l]*f[r]
]
于是我写了一发,(TLE)了两个点……
尼玛还卡快速幂求逆元?(出题人拖出去绕树三匝
可以预处理逆元,这里有一个较好的处理阶乘逆元的方法:这儿是证明
Inv[n]=ksm(Fac[n],p-2);
for(int i=n-1;i>=0;i--) Inv[i]=(Inv[i+1]%p*(i+1)%p)%p;
现在是代码时间~:
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int f=1,w=0;char x=0;
while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
return w*f;
}
const int N=1e6+10;
int n,p,Fac[N],f[N],Log[N],Inv[N];
inline int ksm(int b,int k)
{
int a=b;if(!k) return 1;k--;
while(k)
{
if(k&1) a=(a%p*b%p+p)%p;
b=(b%p*b%p+p)%p;k>>=1;
}
return a%p;
}
inline int C(int n,int m)
{
if(m>n) return 0;
return ((Fac[n]%p*Inv[m]%p*Inv[n-m]%p)%p+p)%p;
}
inline int LC(int n,int m)
{
if(!m) return 1;
return ((C(n%p,m%p)%p*LC(n/p,m/p)%p)%p+p)%p;
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("A.in","r",stdin);
#endif
n=read(),p=read();Fac[1]=1;Log[0]=-1;
for(int i=1;i<=n;i++) Log[i]=Log[i>>1]+1;
for(int i=2;i<=n;i++) Fac[i]=(Fac[i-1]%p*i%p)%p;
int l=1,r=1;f[1]=f[2]=1,f[3]=2;Inv[n]=ksm(Fac[n],p-2);
for(int i=n-1;i>=0;i--) Inv[i]=(Inv[i+1]%p*(i+1)%p)%p;
for(int i=4;i<=n;i++)
{
if(i-(1<<Log[i])+1<=1<<(Log[i]-1)) l++; else r++;//相当于看该节点放在哪颗子树中
f[i]=(LC(i-1,l)%p*(f[l]%p*f[r]%p+p)%p+p)%p;
}
printf("%lld",f[n]);
}