Luogu P2606 [ZJOI2010]排列計數___組合計數+lucas定理+樹形dp

題目大意:

稱一個1,2,…,N的排列P1,P2…,Pn是Magic的,當且僅當2<=i<=N時,Pi>Pi/2. 計算1,2,…N的排列中有多少是Magic的,答案可能很大,只能輸出模P以後的值
在這裏插入圖片描述

分析:

發現我們以1爲根,對於一個點x而言,以(x*2)爲左兒子,x2+1x*2+1爲右兒子,
然後去構造出一顆點編號全部&lt;=n&lt;=n的二叉樹,
然後我們發現,
我們現在要求的,就是對於根到任意一個結點的路徑而言都能滿足上面的數是單調遞增的,這樣的數的選取有多少種,數只能從[1,n][1,n]中去選,每個數只能選一次
發現可以dp
fif_i表示以i爲根的子樹有多少種滿足條件的方案
則可以推出fi=flsonfrsonCsz[i]1sz[lson]f_{i}=f_{lson}*f_{rson}*C_{sz[i]-1}^{sz[lson]}
fif_i中有sz[i]sz[i]個數,除去根節點,還要分配sz[lson]sz[lson]個數給左兒子,分配sz[rson]sz[rson]個數給右兒子,那麼選出來的數的方案數顯然就是Csz[i]1sz[lson]C_{sz[i]-1}^{sz[lson]}
然後我們對Csz[i]1sz[lson]C_{sz[i]-1}^{sz[lson]}可以用公式直接做,不過可能會炸longlong,
因爲p是質數,所以我們可以用lucas定理去求Csz[i]1sz[lson]C_{sz[i]-1}^{sz[lson]}
Cxy=Cx/py/pCxmodpymodpC_{x}^{y}=C_{x/p}^{y/p}*C_{xmodp}^{ymodp}

代碼:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章