欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

洛谷P2606 [ZJOI2010]排列计数(组合数 dp)

程序员文章站 2022-09-14 20:00:26
题意 题目链接 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值 Sol 这辈子做不出的计数系列。 一眼小根堆没啥好说的。最关键的一点是:树的形态是可以 ......

题意

题目链接

称一个1,2,...,n的排列p1,p2...,pn是magic的,当且仅当2<=i<=n时,pi>pi/2. 计算1,2,...n的排列中有多少是magic的,答案可能很大,只能输出模p以后的值

sol

这辈子做不出的计数系列。

一眼小根堆没啥好说的。最关键的一点是:树的形态是可以递推出来的。

那么当前点$i$为根节点,大小为$siz[i]$,左/右儿子分别为$ls, rs$

那么$f[i] = c_{siz[i] - 1}^{siz[ls]} f[ls] \times f[rs]$

lucas定理算组合数

#include<cstdio>
//#define int long long 
using namespace std;
const int maxn = 1e6 + 10;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int n, p, fac[maxn] = {1}, ifac[maxn], siz[maxn], f[maxn];
int fastpow(int a, int p, int mod) {
    int base = 1;
    while(p) {
        if(p & 1) base = (1ll * base % mod * a % mod) % mod;
        a = (1ll * a % mod * a % mod) % mod; p >>= 1;
    }
    return base % mod;
}
int c(int n, int m, int p) {
    if(m > n) return 0;
    return 1ll * fac[n] % p * ifac[m] % p * ifac[n - m] % p;
}
int lucas(int n, int m, int p) {
    if(!n || !m) return 1;
    return lucas(n / p, m / p, p) * c(n % p, m % p, p);    
}
main() {
    n = read(); p = read();
    for(int i = 1; i <= n; i++) fac[i] = 1ll * i * fac[i - 1] % p;
    ifac[n] = fastpow(fac[n], p - 2, p);
    for(int i = n; i >= 1; i--) ifac[i - 1] = 1ll * i * ifac[i] % p;
    for(int i = n; i >= 1; i--) {
        siz[i] = 1;
        int ls = (i << 1), rs = (i << 1 | 1);
        if(rs <= n) siz[i] += siz[ls] + siz[rs], f[i] = 1ll * lucas(siz[i] - 1, siz[ls], p) * f[ls] % p * f[rs] % p;    
        else if(ls <= n) siz[i] += siz[ls], f[i] = f[ls];
        else f[i] = 1;
    }
    printf("%d", f[1]);
    return 0;
}
/*
999999 1000000007
*/