求有多少 \(n\) 排列满足 \(p_i \ge p_{[i/2]}\)。
这个性质就是小根堆的性质
设 \(numleft[i],numright[i]\) 表示大小为 \(i\) 的完全二叉树,其左孩子和右孩子的子树的大小,这个很容易递推出来
设 \(f[i]\) 表示大小为 \(i\) 的完全二叉树的答案,则 \(f[i]=\binom{i-1}{numleft[i]} f[numleft[i]]\cdot f[numright[i]]\)
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, m;
int qpow(int p, int q)
{
return (q & 1 ? p : 1) * (q ? qpow(p * p % m, q / 2) : 1) % m;
}
int inv(int p)
{
return qpow(p, m - 2);
}
const int N = 1e6 + 5;
int fac[N], ifac[N], numleft[N], numright[N], f[N];
int ncr(int n, int k)
{
return fac[n] * ifac[n - k] % m * ifac[k] % m;
}
signed main()
{
cin >> n >> m;
fac[0] = ifac[0] = 1;
for (int i = 1; i < min(N, m); i++)
fac[i] = fac[i - 1] * i % m;
ifac[min(N, m) - 1] = inv(fac[min(N, m) - 1]);
for (int i = min(N, m) - 2; i; i--)
ifac[i] = ifac[i + 1] * (i + 1) % m;
for (int i = 2; i <= n; i++)
{
int p, q, t = i;
while (t)
q = p, p = t & 1, t >>= 1;
numleft[i] = numleft[i - 1];
numright[i] = numright[i - 1];
if (p == q)
numright[i]++;
else
numleft[i]++;
}
f[0] = 1;
f[1] = 1;
for (int i = 2; i <= n; i++)
{
f[i] = ncr(i - 1, numleft[i]) * f[numleft[i]] % m * f[numright[i]] % m;
}
cout << f[n] << endl;
}
原文:https://www.cnblogs.com/mollnn/p/14336931.html