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;
}

浙公网安备 33010602011771号