Hz的计数问题总结

前言

看见 mod1e9 + 7 就跪了,遂写一篇博客把所有的计数问题、组合数学问题都记录下来...

正片

E. Girl Permutation

Some permutation of length n n n is guessed.

You are given the indices of its prefix maximums and suffix maximums.

Recall that a permutation of length k k k is an array of size k k k such that each integer from 1 1 1 to k k k occurs exactly once.

Prefix maximums are the elements that are the maximum on the prefix ending at that element. More formally, the element a i a_i ai is a prefix maximum if a i > a j a_i > a_j ai>aj for every j < i j < i j<i.

Similarly, suffix maximums are defined, the element a i a_i ai is a suffix maximum if a i > a j a_i > a_j ai>aj for every j > i j > i j>i.

You need to output the number of different permutations that could have been guessed.

As this number can be very large, output the answer modulo 1 0 9 + 7 10^9 + 7 109+7.

感觉好经典的模型,没有想出来是因为一直在纠结两部分分开处理的情况下发生重复怎么办。。

实际上我们可以发现,只要先用组合数 C C C 选出一部分数字放在左边,剩下的部分放在右边,则两部分一定不会发生重复。

但是一定存在一种方案使得它们摆放起来满足方案吗?

答案是YES,这个自己手玩一下就可以发现了,数量够的情况下,排列中的数字两两不同,则总会存在一种方式使得摆放是正确的。

因此分开考虑是合法的。

接下来就是单独考虑左边,我们从右向左(从高到低)遍历 p p p 数组,可以发现每两个相邻的下标中间存在着一些 gap,我们选择一些比 pi 和 pi + 1 都要小的数字填充这些 gap(全排列),所以就是comb(pi + 1 - 2, pi + 1 - pi - 1);

这里的 -2 是因为最大值和次大值已经放在了 pi 和 pi + 1 的位置,故,必须-2.

最后代码如下:

cpp 复制代码
int qpow(int a, int k) {
	a %= mod;
	i64 res = 1 % mod;
	while(k) {
		if (k & 1) res = (i64)res * a % mod;
		a = (i64)a * a % mod;
		k >>= 1;
	}
	return res;
}

int inv(int x) {
	return qpow(x, mod - 2);
}
struct Comb {
    int n;
    vector<int> fac, invfac, pow2;
    Comb(): n(0) {}
    Comb(int _n): n(0) { init(_n); }

    void init(int m) {
        if (m <= n) return;
        fac.resize(m + 1);
        invfac.resize(m + 1);
        pow2.resize(m + 1);

        pow2[0] = 1;
        for (int i = 1; i <= m; i++) {
            pow2[i] = (int)(pow2[i - 1] * 2LL) % mod;
        }
        int start = n > 0 ? n + 1 : 1;
        if (n == 0) {
            fac[0] = invfac[0] = 1;
        }
        for (int i = start; i <= m; i++) {
            fac[i] = (int)((i64)fac[i - 1] * i % mod);
        }
        invfac[m] = qpow(fac[m], mod-2);
        for (int i = m; i > (n == 0 ? 1 : n); i--) {
            invfac[i - 1] = (int)((i64)invfac[i] * i % mod);
        }
        n = m;
    }

    // fac[m]
    int F(int m) {
        if (m > n) init(2 * m);
        return fac[m];
    }
    // invfac[m]
    int iF(int m) {
        if (m > n) init(2 * m);
        return invfac[m];
    }
    // inv[m] = m^{-1}
    int inv(int m) {
        return qpow(m, mod - 2);
    }
    // pow(2, n)
    int P2(int m) {
        if (m <= n) return pow2[m];
        return qpow(2, m);
    }
    // A(n, m) = n! / (n - m)!
    int A(int n_, int m_) {
        if (m_ < 0 || m_ > n_) return 0;
        if (n_ > n) init(2 * n_);
        return (int)( (i64)fac[n_] * invfac[n_ - m_] % mod );
    }
    // C(n, m) = n! / (m! * (n - m)!)
    int C(int n_, int m_) {
        if (m_ < 0 || m_ > n_) return 0;
        if (n_ > n) init(2 * n_);
        return (int)((i64)fac[n_] * invfac[m_] % mod * invfac[n_ - m_] % mod );
    }
};
Comb comb(1e6);

void solve()
{
    int n, m1, m2;
    cin >> n >> m1 >> m2;
    vector<int> p(m1 + 1), s(m2 + 1);
    for (int i = 1; i <= m1; i++) {
        cin >> p[i];
    }
    for (int i = 1; i <= m2; i++) {
        cin >> s[i];
    }
    if (s[m2] != n || p[1] != 1 || s[1] != p[m1]) {
        cout << 0 << endl;
        return;
    }
    int ans = comb.C(n - 1, p.back() - 1);
    for (int i = m1 - 1; i >= 1; i--) {
        int g = p[i + 1] - p[i] - 1;
        (ans *= comb.C(p[i + 1] - 2, g) * comb.F(g) % mod) %= mod;
    }
    for (int i = 1; i <= m2 - 1; i++) {
        int g = s[i + 1] - s[i] - 1;
        (ans *= comb.C(n - s[i] - 1, g) * comb.F(g) % mod) %= mod;
    }
    cout << ans << endl;
}
相关推荐
I_belong_to_jesus10 小时前
C++关于分支预测的优化1
开发语言·c++·算法
Code_Solitude17 小时前
C语言动态内存 + 柔性数组 + 数组名退化
c语言·算法·柔性数组
上海潭龙东海科技有限公司18 小时前
客运站管控之动态孪生技术方案(新增一屏管控模块
数据仓库·人工智能·算法·重构·动态规划
YLCHUP20 小时前
【思维&图论建模】题解:债权(debt)_图论_建模_思路转化_思维题目_C++算法竞赛
c++·笔记·算法·数学建模·贪心算法·深度优先·图论
思麟呀21 小时前
背包DP(01背包+完全背包)
c++·算法
笑容掩饰我的失落1 天前
区块链从小白到精通(七):Solidity 语法精讲——数据类型、函数、修饰器与事件
算法·区块链·php·智能合约·哈希算法
Pniubi1 天前
力扣24. 两两交换链表中的节点(递归)
算法·leetcode·链表
nice_lcj5201 天前
LRU 缓存入门:它是什么、怎么用、面试怎么手写
java·数据结构·缓存·面试·lru
꧁细听勿语情꧂1 天前
直接选择排序,堆排序,冒泡排序,递归快速排序、hoare版本、挖坑法、lomuto前后指针,非递归快速排序
c语言·开发语言·数据结构·算法