P2606 [ZJOI2010]排列計數 分析
阿新 • • 發佈:2022-05-18
題意可以簡化為用 $[1,n]$ 的數,組成一個完全二叉樹,使其滿足小根堆性質,求方案數。
令 $f_i$ 表示在 $i$ 點的方案數,$s_i$ 表示 $i$ 的子節點個數(包括 $i$),於是得出遞推式:
$$f_i=C^{s_i-1}_{s_{i \times 2}} \times f_{i*2} \times f_{i*2+1}
$$
由於 BZOJ 上 $n>m$(可能),所以需要用 Lucas 定理。
namespace LZX { using namespace std; #define int long long const int MAXN=2000015; int fac[MAXN],s[MAXN],f[MAXN]; int math_qpow(int base,int power,int mod) { int res=1; while(power) { if(power&1) { res=res*base%mod; } base=base*base%mod; power>>=1; } return res; } int math_C(int n,int m,int p) { return fac[n]*math_qpow(fac[m]*fac[n-m]%p,p-2,p)%p; } int math_lucas(int n,int m,int p) { if(!m) { return 1; } if(m>n) { return 0; } return math_C(n%p,m%p,p)*math_lucas(n/p,m/p,p)%p; } int _main() { int n,m; scanf("%lld%lld",&n,&m); fac[0]=1; for(int i=1;i<=n;i++) { fac[i]=fac[i-1]*i%m; } fill(s+1,s+n+1,1); for(int i=n;i>=2;i--) { s[i>>1]+=s[i]; } fill(f+n+1,f+n*2+2,1); for(int i=n;i;i--) { f[i]=math_lucas(s[i]-1,s[i<<1],m)%m*f[i<<1]%m*f[i*2+1]%m; } printf("%lld\n",f[1]); return 0; } }