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,我们选择一些比 p[i] 和 p[i + 1] 都要小的数字填充这些 gap(全排列),所以就是comb(p[i + 1] - 2, p[i + 1] - p[i] - 1);

这里的 -2 是因为最大值和次大值已经放在了 p[i] 和 p[i + 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;
}
相关推荐
信奥卷王7 小时前
2025年12月GESPC++二级真题解析(含视频)
算法
Z1Jxxx7 小时前
输入n个数进行排序,要求先按奇偶后按从小到大的顺序排序
数据结构·算法
乐迪信息7 小时前
乐迪信息:船体AI烟火检测,24小时火灾自动预警
人工智能·物联网·算法·目标检测·语音识别
Z1Jxxx7 小时前
整除整除整除
开发语言·c++·算法
Swift社区7 小时前
LeetCode 471 编码最短长度的字符串
算法·leetcode·职场和发展
没有天赋那就反复7 小时前
JAVA length
java·开发语言·算法
Tisfy7 小时前
LeetCode 0712.两个字符串的最小ASCII删除和:反向思维保留最大(动态规划)
算法·leetcode·动态规划·字符串·dp·子序列
ohoy7 小时前
RedisTemplate 使用之Hash
redis·算法·哈希算法
栈与堆7 小时前
LeetCode-88-合并两个有序数组
java·开发语言·数据结构·python·算法·leetcode·rust
源代码•宸7 小时前
Leetcode—712. 两个字符串的最小ASCII删除和【中等】
开发语言·后端·算法·leetcode·职场和发展·golang·dp