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)为左儿子, x ∗ 2 + 1 x*2+1 x2+1为右儿子,
然后去构造出一颗点编号全部 < = n <=n <=n的二叉树,
然后我们发现,
我们现在要求的,就是对于根到任意一个结点的路径而言都能满足上面的数是单调递增的,这样的数的选取有多少种,数只能从 [ 1 , n ] [1,n] [1,n]中去选,每个数只能选一次
发现可以dp
f i f_i fi表示以i为根的子树有多少种满足条件的方案
则可以推出 f i = f l s o n ∗ f r s o n ∗ C s z [ i ] − 1 s z [ l s o n ] f_{i}=f_{lson}*f_{rson}*C_{sz[i]-1}^{sz[lson]} fi=flsonfrsonCsz[i]1sz[lson]
f i f_i fi中有 s z [ i ] sz[i] sz[i]个数,除去根节点,还要分配 s z [ l s o n ] sz[lson] sz[lson]个数给左儿子,分配 s z [ r s o n ] sz[rson] sz[rson]个数给右儿子,那么选出来的数的方案数显然就是 C s z [ i ] − 1 s z [ l s o n ] C_{sz[i]-1}^{sz[lson]} Csz[i]1sz[lson]
然后我们对 C s z [ i ] − 1 s z [ l s o n ] C_{sz[i]-1}^{sz[lson]} Csz[i]1sz[lson]可以用公式直接做,不过可能会炸longlong,
因为p是质数,所以我们可以用lucas定理去求 C s z [ i ] − 1 s z [ l s o n ] C_{sz[i]-1}^{sz[lson]} Csz[i]1sz[lson]
C x y = C x / p y / p ∗ C x m o d p y m o d p C_{x}^{y}=C_{x/p}^{y/p}*C_{xmodp}^{ymodp} Cxy=Cx/py/pCxmodpymodp

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;
}

你可能感兴趣的:(C++,树形dp,排列组合,lucas定理)