題目大意:
稱一個1,2,…,N的排列P1,P2…,Pn是Magic的,當且僅當2<=i<=N時,Pi>Pi/2. 計算1,2,…N的排列中有多少是Magic的,答案可能很大,只能輸出模P以後的值
分析:
發現我們以1爲根,對於一個點x而言,以(x*2)爲左兒子,爲右兒子,
然後去構造出一顆點編號全部的二叉樹,
然後我們發現,
我們現在要求的,就是對於根到任意一個結點的路徑而言都能滿足上面的數是單調遞增的,這樣的數的選取有多少種,數只能從中去選,每個數只能選一次
發現可以dp
設表示以i爲根的子樹有多少種滿足條件的方案
則可以推出
中有個數,除去根節點,還要分配個數給左兒子,分配個數給右兒子,那麼選出來的數的方案數顯然就是
然後我們對可以用公式直接做,不過可能會炸longlong,
因爲p是質數,所以我們可以用lucas定理去求
即
代碼:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)
#define lson(x) x * 2
#define rson(x) x * 2 + 1
#define N 1000005
using namespace std;
typedef long long ll;
int fac[N], inv[N], dp[N], sz[N], n, mo;
void Pre_Work() {
fac[0] = 1; rep(i, 1, n) fac[i] = (ll)fac[i - 1] * i % mo;
inv[0] = inv[1] = 1; rep(i, 2, n) inv[i] = (ll)(mo - mo / i) * inv[mo % i] % mo;
inv[0] = 1; rep(i, 1, n) inv[i] = (ll)inv[i - 1] * inv[i] % mo;
}
int C(int n, int m) {
if (n < m) return 0;
if (n > mo || m > mo) return 1ll * C(n / mo, m / mo) * C(n % mo, m % mo) % mo;
return 1ll * fac[n] * inv[n - m] % mo * inv[m] % mo;
}
void dfs(int x) {
if (x > n) return;
sz[x] = 1; dp[x] = 1 % mo;
dfs(lson(x)); if (lson(x) <= n) dp[x] = (ll)dp[x] * dp[lson(x)] % mo, sz[x] += sz[lson(x)];
dfs(rson(x)); if (rson(x) <= n) dp[x] = (ll)dp[x] * dp[rson(x)] % mo, sz[x] += sz[rson(x)];
if (lson(x) <= n) dp[x] = (ll)dp[x] * C(sz[x] - 1, sz[lson(x)]) % mo;
else if (rson(x) <= n) dp[x] = (ll)dp[x] * C(sz[x] - 1, sz[rson(x)]) % mo;
}
int main() {
scanf("%d %d", &n, &mo);
Pre_Work();
dfs(1);
dp[1] =(dp[1] % mo + mo) % mo;
printf("%d\n", dp[1]);
return 0;
}