20260404 做题记录
CF2144E1
求原序列的子序列,使得前缀严格最大值集合和后缀严格最大值集合分别不变。
\(n\le 3\times10^3\)。
小清新 dp 题目。决斗的时候场切了。
分别记录 \(f_{i,j}\) 和 \(g_{i,j}\) 代表前缀/后缀第 \(j\) 个最大值的方案数。预处理 \(p, t\) 作为前后缀最大值集合。
考虑转移,这里只说 \(f\),\(g\) 是同理的。
- 考虑不选自己。\(f_{i,j}\gets f_{i-1,j}\)
- 选上自己。\(f_{i,j}\gets f_{i-1, j} [a_i=p_j]\)
- 选上自己,但是自己不算在前缀最大值里。\(f_{i,j}\gets f_{i-1,j}[a_i\le p_j]\)
然后答案你枚举子序列的最大值加起来就行了。时间复杂度 \(O(n^2)\)
CF2144E2
上面,但是 \(n\le 3\times 10^5\)。
没写这个,不过盯着看了两秒出来了。
那你还说啥了,你把这个放到线段树上维护不就得了。时间复杂度 \(O(n\log n)\)
CF1579G
\(n\) 条给定长度的线段,首尾相连,可以向左或向右,求最小覆盖长度。
\(n \le 10^4, a_i \le 10^3\)。
随机随到的,不看题解切掉了。
\(f_{i,j}\) 代表在第 \(j\) 位置的最小长度。
考虑转移,固定左端点为 0 的话就直接转就行了,放个我写的转移部分代码。
void update (int i, int j, int x, int k) {
if (0 > j + x || j + x > M) return ;
f [i + 1] [j + x] = min (f [i + 1] [j + x], k);
}
void Turn () {
cin >> n;
for (int i = 1 ; i <= n ; i ++) cin >> a [i];
for (int i = 0 ; i <= n ; i ++)
for (int j = 0 ; j <= M ; j ++)
f [i] [j] = inf;
f [0] [0] = 0;
for (int i = 0 ; i < n ; i ++) {
for (int j = 0 ; j <= M ; j ++) {
if (f [i] [j] < j + a [i]) update (i, j, a [i], j + a [i]);
if (f [i] [j] >= j + a [i]) update (i, j, a [i], f [i] [j]);
if (j < a [i]) update (i, j, -j, f [i] [j] - j + a [i]);
if (j >= a [i]) update (i, j, -a [i], f [i] [j]);
}
}
int minx = inf;
for (int i = 0 ; i <= M ; i ++) minx = min (minx, f [n] [i]);
cout << minx << endl;
}
还是很好想的,不知道为啥 2200.

浙公网安备 33010602011771号