Codeforces 1132C - Painting the Fence

题目链接:Codeforces 1132C

首先预处理所有的询问,统计每个段有多少个人涂,即对每个\([l_i, r_i]\)区间加一。

我们需要\(q-2\)个人,反向考虑我们删掉哪两个。枚举删掉的第一个,把他的区间贡献从预处理的数组中删去,然后用一个新的数组\(c\)来记录哪些点只有一个人涂,即\(c_i = [a_i = 1]\)。接着我们去枚举除了第一个人之外的所有人,如果把他们删掉,那么会有多少个点变成0,即对\(c_i\)求前缀和,这个值为\(pre[r_i] - pre[l_i - 1]\)。我们希望这个值尽可能的小,因此最小的那个就是我们要删除的第二个点。

这种固定一个(枚举),求另一个的方法是一种常用方法。

时间复杂度\(O(nq + q^2)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAX_N = 5000 + 5;
int n, q, ans, l[MAX_N], r[MAX_N], itv[MAX_N], c[MAX_N];
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= q; ++i) scanf("%d%d", l + i, r + i);
for (int i = 1; i <= q; ++i) {
for (int j = l[i]; j <= r[i]; ++j) {
++itv[j];
}
}
for (int i = 1; i <= q; ++i) {
for (int j = l[i]; j <= r[i]; ++j) --itv[j];
int sum = 0;
for (int j = 1; j <= n; ++j) {
c[j] = (itv[j] == 1) + c[j - 1];
sum += (itv[j] > 0);
}
int minWork = INT_MAX;
for (int j = 1; j <= q; ++j) {
if (i == j) continue;
minWork = min(minWork, c[r[j]] - c[l[j] - 1]);
}
ans = max(sum - minWork, ans);
for (int j = l[i]; j <= r[i]; ++j) ++itv[j];
}
printf("%d\n", ans);
return 0;
}