组合数学

\(\text{luogu-4921}\)

\(n\) 对情侣来到电影院观看电影。在电影院,恰好留有 \(n\) 排座位,每排包含 \(2\) 个座位,共 \(2×n\) 个座位。

现在,每个人将会随机坐在某一个位置上,且恰好将这 \(2 × n\) 个座位坐满。

如果一对情侣坐在了同一排的座位上,那么我们称这对情侣是和睦的。

你的任务是求出当 \(k = 0, 1, ... , n\) 时,共有多少种不同的就坐方案满足恰好\(k\) 对情侣是和睦的。

两种就坐方案不同当且仅当存在一个人在两种方案中坐在了不同的位置。不难发现,一共会有 \((2n)!\) 种不同的就坐方案。

由于结果可能较大,因此输出对 \(998244353\) 取模的结果。

\(1 \le T \le 1000\)\(1 \le n \le 1000\)


\(f_x\) 表示 \(x\) 对情侣都错开的方案数,那么答案即为:

\[\binom{n}{k}\times \binom{n}{k} \times k! \times 2^k \times f_{n-k} \mod MOD \]

其中:

  • 第一个 \(\binom{n}{k}\) 表示 \(n\) 对座位中选出 \(k\) 个。
  • 第二个 \(\binom{n}{k}\) 表示 \(n\) 对情侣中选出 \(k\) 个。
  • \(k!\) 表示 \(k\) 对和睦情侣座位顺序可以随意排列。
  • \(2^k\) 表示 \(k\) 对和睦情侣左右顺序可以随意调换。
  • \(f_{n-k}\) 表示剩下 \(n-k\) 对情侣都错开的方案数。

那么我们的目标变为求 \(f_x\) 的值。

我们在第一排放不和睦的两个人,由于他们坐一起了,则他们的情侣都无法配对了,所以他们以及他们的情侣是男是女都无所谓。下面开始分类讨论:

  • 若他们的两个情侣坐在了一起,则我们需要再选出一排给他们,方案数为 \(2(x-2)f_{x-2}\)
  • 否则,我们不妨将他们看成一对匹配失败的情侣,方案数为 \(f_{x-1}\)

\(2x\) 个人中选出不为情侣的两个人的方案数为 \(2x(2x-2)=4x(x-1)\)

因此可以得出 \(f_x\) 的递推式:

\[f_x = 4x(x-1)\left[f_{x-1} + 2(x-1)f_{x-2}\right] \]

提前预处理 \(f_x\) 即可,实现精细的话整体复杂度为 \(O(Tn)\),不过我的是 \(O(Tn \log n)\),不影响。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAXN 5000005
#define MOD 998244353

ll read() {
    ll x = 0, f = 1;
    char c = getchar();
    while(c < 48 || c > 57) { if(c == 45) f = -1; c = getchar(); }
    while(c >= 48 && c <= 57) { x = (x << 3) + (x << 1) + (c - 48); c = getchar(); }
    return x * f;
}

ll T, n, p[MAXN], inv[MAXN], pv[MAXN], f[MAXN];

ll qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % MOD;
        a = a * a % MOD, b >>= 1;
    }
    return res;
}

int main() {
    inv[1] = p[0] = pv[0] = 1;
    for(int i = 2; i < MAXN; i ++) inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    for(int i = 1; i < MAXN; i ++)
        p[i] = p[i - 1] * i % MOD, pv[i] = pv[i - 1] * inv[i] % MOD;
    f[0] = 1, f[1] = 0;
    for(int i = 2; i < MAXN; i ++) 
        f[i] = 2 * i * (2 * i - 2) % MOD * (f[i - 1] % MOD + (2 * i - 2) * f[i - 2] % MOD) % MOD;
    T = read();
    while(T --) {
        n = read();
        for(int k = 0; k <= n; k ++) 
            cout << (p[n] * pv[k] % MOD * pv[n - k] % MOD * p[n] % MOD * pv[n - k] % MOD 
                * qpow(2, k) % MOD * f[n - k] % MOD) % MOD << "\n";
    }
    return 0;
}

\(\text{luogu-4931}\)

\(n\) 对情侣来到电影院观看电影。在电影院,恰好留有 \(n\) 排座位,每排包含 \(2\) 个座位,共 \(2n\) 个座位。

现在,每个人将会随机坐在某一个位置上,且恰好将这 \(2n\) 个座位坐满。

如果一对情侣坐在了同一排的座位上,那么我们称这对情侣是和睦的。

你的任务是求出共有多少种不同的就坐方案满足恰好k 对情侣是和睦的。

两种就坐方案不同当且仅当存在一个人在两种方案中坐在了不同的位置。不难发现,一共会有 \((2n)!\) 种不同的就坐方案。

由于结果可能较大,因此输出对 \(998244353\) 取模的结果。

\(1 \leq T \leq 2 \times 10^5, 1 \leq n \leq 5 \times 10^6, 0 \leq k \leq n\)


这题凭啥是黑,加强在哪。

跟 luogu-4921 几乎一样,带 \(O(\log n)\) 仍然能过。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAXN 5000005
#define MOD 998244353

ll read() {
    ll x = 0, f = 1;
    char c = getchar();
    while(c < 48 || c > 57) { if(c == 45) f = -1; c = getchar(); }
    while(c >= 48 && c <= 57) { x = (x << 3) + (x << 1) + (c - 48); c = getchar(); }
    return x * f;
}

ll T, n, k, p[MAXN], inv[MAXN], pv[MAXN], f[MAXN];

ll qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % MOD;
        a = a * a % MOD, b >>= 1;
    }
    return res;
}

int main() {
    inv[1] = p[0] = pv[0] = 1;
    for(int i = 2; i < MAXN; i ++) inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    for(int i = 1; i < MAXN; i ++)
        p[i] = p[i - 1] * i % MOD, pv[i] = pv[i - 1] * inv[i] % MOD;
    f[0] = 1, f[1] = 0;
    for(ll i = 2; i < MAXN; i ++) 
        f[i] = 2 * i * (2 * i - 2) % MOD * (f[i - 1] % MOD + (2 * i - 2) * f[i - 2] % MOD) % MOD;
    T = read();
    while(T --) {
        n = read(), k = read();
        cout << p[n] * pv[k] % MOD * pv[n - k] % MOD * p[n] % MOD * pv[n - k] % MOD 
            * qpow(2, k) % MOD * f[n - k] % MOD << "\n";
    }
    return 0;
}
posted @ 2026-03-05 19:18  So_noSlack  阅读(4)  评论(0)    收藏  举报