题目
称一个的排列是Magic的,当且仅当时,. 计算的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值
为一个质数
dp
对于等式,其即为最小堆(数组形式)的充要条件
问题即转换为求可以构成多少个最小堆
考虑构成的最小堆个数可以知道,根一定是1,以此结构进行
由于等式限制,其一定为完全二叉树,则根的左孩子个数确定
则转移方程为
由于可能有,故需要阶乘逆元与定理配合计算
---
对于l的计算可以考虑满完全二叉树结构进行统计
本代码是在遇到时更新(从左向右,这一层铺满所增加的左孩子)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
int N, MOD;
LL dp[1000005];
LL fac[1000005], inv_fac[1000005];
void pre_fac();
LL qpow(LL, LL);
LL C(LL, LL);
int main(){
cin >> N >> MOD;
int i, l, m, cnt;
pre_fac();
dp[1] = 1, dp[0] = 1;
for(i = 2, l = m = 0; i <= N; i++){
if((i & (i - 1)) == 0)
cnt = 1 << m, m++;
if(cnt) l++, cnt--;
dp[i] = C(i - 1, l) * dp[l] % MOD * dp[i - 1 - l] % MOD;
}
cout << dp[N] << endl;
return 0;
}
LL C(LL n, LL m){
if(m == 0 && m == n) return 1;
if(m > n) return 0;
if(m == 1) return n;
if(n < MOD) return fac[n] * inv_fac[n - m] % MOD * inv_fac[m] % MOD;
return C(n / MOD, m / MOD) * C(n % MOD, m % MOD) % MOD;
}
void pre_fac(){
int i;
fac[0] = 1;
for(i = 1; i <= N; i++)
fac[i] = fac[i - 1] * i % MOD;
inv_fac[N] = qpow(fac[N], MOD - 2);
for(i = N - 1; i >= 0; i--)
inv_fac[i] = (i + 1) * inv_fac[i + 1] % MOD;
}
LL qpow(LL x, LL m){
LL res = 1;
while(m){
if(m & 1) res = res * x % MOD;
x = x * x % MOD;
m >>= 1;
}
return res;
}