Loading

XMOJ 四月月赛 T3 旅行 题解

我们首先尝试挖掘这个分组的性质。

我们发现,我们可以把在同一个组的夫妻和不在同一个组的夫妻分开来处理。

这里,分开之后我们只需要让一种情况有顺序,另外一种不能有顺序。如果两个没有顺序 / 有顺序的序列合并,一定会出现漏算 / 多算。
所以为了方便,我们可以把第二种情况看作有顺序。
思考:为什么不能把第一种情况看作有顺序呢?

首先假设总共有 \(i\) 组,这里 \(i\) 是我们枚举的数。


\(\text{Situation 1}\):同一个组的夫妻情况数

假设有 \(x\) 组这种夫妻。

我们需要把这 \(x\) 组夫妻划分进不同的组别里,组内没有顺序,组外也没有顺序。

抽象化一下,我们需要把 \(x\) 个互不相同的数分组,组内、组外均没有顺序。

仔细思考,这不是第二类斯特林数吗?直接 \(f_{i, j} = f_{i - 1, j - 1} + j \times f_{i - 1, j}\) 实现 \(\mathcal{O}(n^2)\) 递推即可。


\(\text{Situation 2}\):不同组的夫妻情况数

这非常容易。

由于我们固定了这种情况有顺序,所以我们完全可以对每一个夫妻单独划分组别。每一个夫妻的方案数为 \(i \times (i - 1)\),即总方案数为 \(p_{i, x} = (i(i - 1))^x\)


接下来合体。

在预处理完前两个的情况后,我们首先枚举 \(i\) 表示整体的组别数量。

接着,我们枚举分在同一组的夫妻 \(j\)。于是,\(n - j\) 对夫妻不在同一组。这样的方案数显然是 \(\binom{n}{j}\)

再结合开头的结论,我们得出答案:

\[\sum_{i = 1}^n \sum_{j = 1}^i \binom{n}{j} \times f_{i, j} \times p_{i,j} \]


感觉非常的数学。值得记录。

/*******************************
| Author:  DE_aemmprty
| Problem: Nastya and Unexpected Guest
| Contest: Luogu
| URL:     https://www.luogu.com.cn/problem/CF1340C
| When:    2024-05-05 21:48:42
| 
| Memory:  250 MB
| Time:    1000 ms
*******************************/
#include <bits/stdc++.h>
using namespace std;

long long read() {
    char c = getchar();
    long long x = 0, p = 1;
    while ((c < '0' || c > '9') && c != '-') c = getchar();
    if (c == '-') p = -1, c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x * p;
}

const int N = 607;
const long long mod = 1e9 + 7;

int n;
long long f[N][N];
long long p[N][N];
long long fac[N];

long long ksm(long long x, long long y) {
    long long res = 1;
    for (; y; y >>= 1, (x *= x) %= mod)
        if (y & 1)
            (res *= x) %= mod;
    return res;
}

long long C(long long x, long long y) {
    return (x < y ? 0 : fac[x] * ksm(fac[y] * fac[x - y] % mod, mod - 2) % mod);
}

void solve() {
    n = read();
    f[0][0] = fac[0] = 1;
    for (int i = 1; i <= n; i ++)
        fac[i] = fac[i - 1] * i % mod;
    for (int i = 1; i <= n; i ++)
        for (int j = 0; j <= i; j ++)
            f[i][j] = (f[i - 1][j - 1] + j * f[i - 1][j] % mod) % mod;
    for (int i = 1; i <= n; i ++)
        for (int j = 0; j <= n; j ++)
            p[i][j] = ksm(i * (i - 1) % mod, j) % mod;
    long long ans = 0;
    for (int i = 1; i <= n; i ++) for (int j = 1; j <= i; j ++)
        (ans += C(n, i) * f[i][j] % mod * p[j][n - i] % mod) %= mod;
    cout << ans;
}

signed main() {
    freopen("trip.in", "r", stdin);
    freopen("trip.out", "w", stdout);
    int t = 1;
    while (t --) solve();
    return 0;
}
posted @ 2024-05-06 23:08  DE_aemmprty  阅读(113)  评论(0)    收藏  举报