洛谷 P2606 [ZJOI2010]排列计数 分析
题意可以简化为用 $[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}
$$
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;
}
}

浙公网安备 33010602011771号