XMOJ 四月月赛 T3 旅行 题解

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

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

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

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


Situation 1:同一个组的夫妻情况数

假设有 x 组这种夫妻。

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

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

仔细思考,这不是第二类斯特林数吗?直接 fi,j=fi1,j1+j×fi1,j 实现 O(n2) 递推即可。


Situation 2:不同组的夫妻情况数

这非常容易。

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


接下来合体。

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

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

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

i=1nj=1i(nj)×fi,j×pi,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;
}

作者:DE_aemmprty

出处:https://www.cnblogs.com/aemmprty/p/18176176

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   DE_aemmprty  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示