20260404 做题记录

CF2144E1

求原序列的子序列,使得前缀严格最大值集合和后缀严格最大值集合分别不变。

\(n\le 3\times10^3\)


小清新 dp 题目。决斗的时候场切了。

分别记录 \(f_{i,j}\)\(g_{i,j}\) 代表前缀/后缀第 \(j\) 个最大值的方案数。预处理 \(p, t\) 作为前后缀最大值集合。

考虑转移,这里只说 \(f\)\(g\) 是同理的。

  1. 考虑不选自己。\(f_{i,j}\gets f_{i-1,j}\)
  2. 选上自己。\(f_{i,j}\gets f_{i-1, j} [a_i=p_j]\)
  3. 选上自己,但是自己不算在前缀最大值里。\(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.

posted @ 2026-04-05 01:56  棉裤Manki  阅读(4)  评论(0)    收藏  举报