D. Zztrans 的班级合照

D. Zztrans 的班级合照

传送门

当时看到这个题就想到了卡特兰来着,但具体不会推导(

还想到了这个问题,但忘了他就是卡特兰(我在说什么/我在干什么

有一个长度为2n的01串,01各有n个,对于1到2n的每个位置,包括这个位置之前1的个数不少于0的个数

放一个卡特兰参考博客

只考虑各n个的这个条件:把01看做坐标系上的操作,1是向斜上方走一步,0向斜下方走一步,最后一定会走到(2n, 0),此时的方案数为 C 2 n n C^{n}_{2n} C2nn

在这里插入图片描述

考虑对于任意前缀1的个数不少于0的个数,转化到坐标系中 → \to 形成的路径不应该穿过x轴,即直线y=0,即不碰到y=-1,

考虑一但接触到y=-1点,接触点的右边关于y=-1直线上下翻折,得到一条从(0,0)开始到(2n,-2)结束的路径,这样的路径的条数为 C 2 n n − 1 C^{n - 1}_{2n} C2nn1

总方案数: C 2 n n − C 2 n n − 1 C^{n}_{2n} - C^{n - 1}_{2n} C2nnC2nn1
在这里插入图片描述

回到这个问题,考虑每个数都不相同:

只要一直保证第一排的人数一直大于等于第二排的人数就能满足身高递增的需求, 即将在第一排末尾加人和在第二排末尾加人分别看做上述问题的向斜上方走和向斜下方走,就是一个卡特兰

当时在想是不是这里直接乘上阶乘就好了,但样例1和2显示并不是这样,于是就没继续想
(怎么转化回卡特兰待补充嗷(

这里其实直接枚举每种身高的人多少站在第一排,其他站在第二排,进行一个dp

dp[i][j] := 前i个人中有j个在第一排的方案数

只要一直保证第一排的人数一直大于等于第二排的人数就能满足身高递增的需求,即:当i - j > j的时候都不合法

直接避开这些情况做dp, n 2 n^2 n2

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll mod = 998244353;
const ll N = 5010;

ll a[N], dp[N][N], fac[N];

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    fac[0] = 1;
    for (ll i = 1; i < N; ++i) fac[i] = fac[i - 1] * i % mod;

    ll n;
    cin >> n;
    for (ll i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    sort(a + 1, a + n + 1, [](ll x, ll y) { return x < y; });

    vector<ll> vec;
    for (ll i = 1; i <= n;) {
        ll cnt = 1;
        ll r = i + 1;
        while (r <= n && a[i] == a[r]) cnt++, r++;
        vec.push_back(cnt);
        i = r;
    }

    ll sum = 0;
    dp[0][0] = 1;
    for (auto num : vec) {
        sum = sum + num;
        for (ll i = min(n / 2, sum); i >= sum - i; --i) {
            for (ll j = 0; j <= min(num, i); ++j) {
                dp[sum][i] = (dp[sum][i] + dp[sum - num][i - j]) % mod;
            }
        }
    }

    ll ans = dp[n][n / 2];

    for (auto x : vec) ans = ans * fac[x] % mod;
    cout << ans;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值