1 条题解
-
0
直接做。这样设状态可能舒服点。
区间图是弦图,没有环等价于没有三元环,也就是说每个坐标至多被覆盖 次,并且要求所有选中线段的并是连续区间。
先把线段按照左端点升序排序(相等时按照右端点升序排序),然后逐个加入,假设当前最大和次大的右端点分别为 ,现加入 ,那么需有 (连通)且 (覆盖不超过 次)。
因此设 表示选了 条线段,最后选的是 (显然 为 之一),另一个有效右端点为 。能转移到的 是一段连续区间,且新状态的 ,转移时对行做差分即可,时间复杂度 ,用滚动数组,空间复杂度 。
注意为了避免算重,求出来合法 的区间左端点要和 取 。
#include <bits/stdc++.h> #include "segment.h" using namespace std; const int P = 998244353; struct range { int l, r, i; }; inline void add(int &x, int y) { (x += y) >= P && (x -= P); } void init(int c, int t) {} vector<int> segment(int n, int m, int k, vector<int> l, vector<int> r) { vector<range> a; for (int i = 0; i < n; i++) a.push_back({l[i], r[i], i}); sort(a.begin(), a.end(), [](const range &x, const range &y) { return x.l == y.l ? x.r < y.r : x.l < y.l; }); vector<int> pt(m + 1, n); for (int i = 0; i <= m; i++) for (int j = i ? pt[i - 1] : 0; j < n; j++) if (a[j].l > i) { pt[i] = j; break; } vector<vector<int> > f(m + 1, vector<int>(n)), d(m + 1, vector<int>(n + 1)); vector<int> ans(k + 1); fill(f[0].begin(), f[0].end(), 1), ans[1] = n; for (int s = 1; s < k; s++) { for (int i = 0; i <= m; i++) fill(d[i].begin(), d[i].end(), 0); for (int i = 0; i <= m; i++) for (int j = 0; j < n; j++) if (f[i][j]) { auto [mn, mx] = minmax(a[j].r, i); int l = max(pt[mn], j + 1), r = pt[mx] - 1; if (l <= r) add(d[mx][l], f[i][j]), add(d[mx][r + 1], P - f[i][j]); } for (int i = 0; i <= m; i++) for (int j = 0, c = 0; j < n; j++) add(c, d[i][j]), f[i][j] = c, add(ans[s + 1], c); } return ans; }
- 1
信息
- ID
- 12601
- 时间
- 2500ms
- 内存
- 600MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者