首页 > 其他 > 详细

[ZJOI2010] 排列计数 - 组合数学

时间:2021-01-27 22:44:22      阅读:71      评论:0      收藏:0      [点我收藏+]

[ZJOI2010] 排列计数 - 组合数学

Description

求有多少 \(n\) 排列满足 \(p_i \ge p_{[i/2]}\)

Solution

这个性质就是小根堆的性质

\(numleft[i],numright[i]\) 表示大小为 \(i\) 的完全二叉树,其左孩子和右孩子的子树的大小,这个很容易递推出来

\(f[i]\) 表示大小为 \(i\) 的完全二叉树的答案,则 \(f[i]=\binom{i-1}{numleft[i]} f[numleft[i]]\cdot f[numright[i]]\)

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

#define int long long

int n, m;

int qpow(int p, int q)
{
    return (q & 1 ? p : 1) * (q ? qpow(p * p % m, q / 2) : 1) % m;
}

int inv(int p)
{
    return qpow(p, m - 2);
}

const int N = 1e6 + 5;

int fac[N], ifac[N], numleft[N], numright[N], f[N];

int ncr(int n, int k)
{
    return fac[n] * ifac[n - k] % m * ifac[k] % m;
}

signed main()
{
    cin >> n >> m;

    fac[0] = ifac[0] = 1;
    for (int i = 1; i < min(N, m); i++)
        fac[i] = fac[i - 1] * i % m;
    ifac[min(N, m) - 1] = inv(fac[min(N, m) - 1]);
    for (int i = min(N, m) - 2; i; i--)
        ifac[i] = ifac[i + 1] * (i + 1) % m;


    for (int i = 2; i <= n; i++)
    {
        int p, q, t = i;
        while (t)
            q = p, p = t & 1, t >>= 1;
        numleft[i] = numleft[i - 1];
        numright[i] = numright[i - 1];
        if (p == q)
            numright[i]++;
        else
            numleft[i]++;
    }

    f[0] = 1;
    f[1] = 1;

    for (int i = 2; i <= n; i++)
    {
        f[i] = ncr(i - 1, numleft[i]) * f[numleft[i]] % m * f[numright[i]] % m;
    }
    cout << f[n] << endl;
}

[ZJOI2010] 排列计数 - 组合数学

原文:https://www.cnblogs.com/mollnn/p/14336931.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!