题意:求有多少个排列满足:对于每一个 \(2 \le i \le n\),\(a_i > a_{\frac{i}{2}}\)。
首先我们会发现这些数之间的大小关系形成了一个完全二叉树,因为每一个 \(a_i\) 都必须小于 \(a_{2\times i}\) 和 \(a_{2 \times i + 1}\)。
会发现本题的方案和每个位置具体的值并没有关系,而只在乎两个位置之间的大小关系。
那么我们设 \(dp_i\) 表示在大小为 \(i\) 的完全二叉树内放 \(i\) 个不同的数的方案数(注意这里只考虑两数之间的大小关系,而不考虑每个数具体的值)。根节点只能放最小的数,假设根节点的左子树有 \(L\) 个点,右子树有 \(R\) 个点。在剩下的 \(i-1\) 个数中,可以选 \(L\) 个放到左子树中,有 \(C_{i-1}^{L}\) 种选法,其余的放到右子树。所以有:
\[dp_i=C_{i-1}^{L} \times dp_L \times dp_{R} \]因为 \(n,m\) 有可能大于 \(mod\),所以需要用 \(\text{Lucas}\) 定理来计算组合数,时间复杂度 \(O(n \log n)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 1e6 + 10;
int n,mod,dp[MAXN],inv[MAXN],fac[MAXN],invfac[MAXN];
inline int C(int n,int m)
{
if(n < m || m < 0) return 0;
return fac[n] * invfac[m] % mod * invfac[n - m] % mod;
}
inline int Lucas(int n,int m)
{
if(n < mod && m < mod) return C(n,m);
return Lucas(n / mod,m / mod) * C(n % mod,m % mod) % mod;
}
signed main()
{
cin >> n >> mod;
inv[0] = fac[0] = invfac[0] = 1;
inv[1] = fac[1] = invfac[1] = 1;
for(int i = 2;i <= min(n,mod - 1);i++)
{
fac[i] = fac[i - 1] * i % mod;
inv[i] = inv[mod % i] * (mod - mod / i) % mod;
invfac[i] = invfac[i - 1] * inv[i] % mod;
}
dp[1] = dp[0] = 1;
for(int i = 2;i <= n;i++)
{
int L = (1 << (__lg(i) - 1)) - 1,R = (1 << (__lg(i) - 1)) - 1;
if((i - L - R - 1) <= (1 << (__lg(i) - 1))) L += (i - L - R - 1);
else L += (1 << (__lg(i) - 1)),R = i - L - 1;
dp[i] = Lucas(i - 1,L) * dp[L] % mod * dp[R] % mod;
}
cout << dp[n];
return 0;
}
标签:return,invfac,int,题解,ZJOI2010,P2606,MAXN,dp,mod
From: https://www.cnblogs.com/Creeperl/p/18044469