题目描述
称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值
输入输出格式
输入格式:
输入文件的第一行包含两个整数 n和p,含义如上所述。
输出格式:
输出文件中仅包含一个整数,表示计算1,2,⋯, ���的排列中, Magic排列的个数模 p的值。
输入输出样例
说明
100%的数据中,1 ≤N ≤ 10^6, P≤ 10^9,p是一个质数。
挺有趣的一道题,玩着玩着发现这题是在统计小根堆的个数。
那么就简单了,对于一个n个点的小根堆来说,它的形态是固定的,并且根的左右子树都是一个小根堆。
这样我们每一次去找根的左右子树分别是几个点的堆,选一些数放到左子树去,剩下的到右子树去就好了。所以我们预处理一个组合数,乘法原理乘一下方案就行了。
要注意左右子树大小不是很好直接计算,建议实时模拟维护。
#include<cstdio>
#define LL long long
using namespace std;
const int maxn = 1e6 + 5;
int n, dp[maxn], p, pos, siz;
int step[maxn], inv[maxn];
LL fpow(LL a, int b) {
LL ans = 1;
while(b) {
if(b & 1) ans = ans * a % p;
a = a * a % p, b >>= 1;
}
return ans;
}
void pre(int n) {
step[0] = inv[0] = 1;
for(register int i = 1; i <= n; ++i)
step[i] = 1ll * step[i - 1] * i % p;
inv[n] = fpow(step[n], p - 2);
for(register int i = n - 1; i >= 1; --i)
inv[i] = 1ll * inv[i + 1] * (i + 1) % p;
}
LL C(int n, int m) {
return (1ll * step[n] * inv[n - m] % p) * inv[m] % p;
}
int main() {
scanf("%d%d", &n, &p);
pre(n), dp[1] = dp[0] = 1, siz = pos = 1;
int l = 0, r = 0;
for(register int i = 2; i <= n; ++i) {
if((siz << 1) + 1 < i)
pos = pos << 1, siz = ((pos << 1) - 1);
if(i - siz <= pos) ++l;
else ++r;
dp[i] = (1ll * dp[l] * dp[r] % p) * C(i - 1, l) % p;
}
printf("%d", dp[n]);
return 0;
}
rockdu
没有帐号? 立即注册