题意

称一个BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_小根堆的排列BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_c++_02BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_c++_03的,当且仅当BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_c++_04时,BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_小根堆_05. 计算BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_lucas定理_06的排列中有多少是BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_c++_03的,答案可能很大,只能输出模BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_lucas定理_08以后的值。

题解

这道题最妙的就是把题目转化为求BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_c++_09个点的小根堆数量。

然后就随便DP了。

注意模数可能小于BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_c++_10,要用BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数_c++_11定理。

CODE

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 2000005;

int fac[MAXN], inv[MAXN], mod, n, sz[MAXN];

inline int C(int x, int y) {
if(x < y) return 0;
if(x < mod && y < mod) return 1ll * fac[x] * inv[y] % mod * inv[x-y] % mod;
return 1ll * C(x%mod, y%mod) * C(x/mod, y/mod) % mod;
}

void dfs1(int i) {
if(i > n) return;
sz[i] = 1;
dfs1(i<<1); sz[i] += sz[i<<1];
dfs1(i<<1|1); sz[i] += sz[i<<1|1];
}

int dfs2(int i) {
if(i > n) return 1;
int re = C(sz[i]-1, sz[i<<1]);
re = 1ll * re * dfs2(i<<1) % mod;
re = 1ll * re * dfs2(i<<1|1) % mod;
return re;
}

int main ()
{
scanf("%d%d", &n, &mod);
fac[0] = inv[0] = fac[1] = inv[1] = 1;
for(int i = 2; i <= n; ++i) inv[i] = 1ll * (mod - mod/i) * inv[mod%i] % mod;
for(int i = 2; i <= n; ++i) fac[i] = 1ll * fac[i-1] * i % mod, inv[i] = 1ll * inv[i] * inv[i-1] % mod;
dfs1(1);
printf("%d\n", dfs2(1));
}