AT_awtf2025_b Movies
题目的最优化的子问题是一个匹配的形式,但是转成最小割后也并不好做。事实上我们有一个简单的贪心,维护一个堆,每次在左端点加入区间,然后取出最小的右端点。
但是这样一个点上有很多区间,状态还是非常复杂。考虑进一步把策略放到每个区间上,事实上可以把所有区间按照右端点排序,然后所有区间依次尽量靠左选,这样也能最优化答案。
此时其实就可以 dp 了。对于最终每个点的匹配状态,取出一个极长连续段 \([l,r]\),那么我们只需要考虑 \(l_i\in [l,r]\) 的所有区间,而这个实际上非常独立,连续段之间互不影响。
令 \(f_{i,l,r}\) 表示现在考虑了 \(1\sim i\) 的区间,当前连续段为 \([l,r]\),且其余位置为空。转移枚举一下区间 \(i\) 最终填了哪个位置即可。总复杂度 \(O(n^5)\),常数非常小,可以通过。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int> ;
const int mod = 998244353;
void Add(int &x, ll y) { x = (x + y) % mod; }
const int kN = 105, kM = 5505;
int n, m;
pii seg[kM];
int f[kM][kN][kN];
int g[kN], s[kN];
int main() {
// freopen("1.in", "r", stdin);
// freopen("1.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i++) cin >> seg[i].first >> seg[i].second;
sort(seg + 1, seg + m + 1,
[&](pii x, pii y) -> bool {
int lx, rx;
int ly, ry;
tie(lx, rx) = x;
tie(ly, ry) = y;
return (rx != ry) ? (rx < ry) : (lx < ly);
});
for(int l = 1; l <= n + 1; l++) f[0][l][l - 1] = 1;
for(int i = 1; i <= m; i++) {
int p, q;
tie(p, q) = seg[i];
memcpy(f[i], f[i - 1], sizeof(f[i]));
for(int l = 1; l <= p; l++) {
for(int r = p; r <= n; r++) {
if((l <= p) && (q <= r)) Add(f[i][l][r], f[i - 1][l][r]);
for(int j = max(l, p); j <= min(r, q); j++) {
Add(f[i][l][r], (ll)f[i - 1][l][j - 1] * f[i - 1][j + 1][r]);
}
}
}
}
int ans = 0;
g[0] = 1;
for(int i = 1; i <= n + 1; i++) {
for(int j = 0; j < i; j++) {
Add(g[i], (ll)g[j] * f[m][j + 1][i - 1]);
Add(s[i], (ll)(i - j - 1) * f[m][j + 1][i - 1] % mod * g[j]);
Add(s[i], (ll)s[j] * f[m][j + 1][i - 1]);
}
}
cout << s[n + 1] << "\n";
return 0;
}
浙公网安备 33010602011771号