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;
}
posted @ 2025-10-10 20:02  CJzdc  阅读(16)  评论(0)    收藏  举报