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}
C2nn−1
总方案数:
C
2
n
n
−
C
2
n
n
−
1
C^{n}_{2n} - C^{n - 1}_{2n}
C2nn−C2nn−1
回到这个问题,考虑每个数都不相同:
只要一直保证第一排的人数一直大于等于第二排的人数就能满足身高递增的需求, 即将在第一排末尾加人和在第二排末尾加人分别看做上述问题的向斜上方走和向斜下方走,就是一个卡特兰
当时在想是不是这里直接乘上阶乘就好了,但样例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;
}