0%

The 2025 ICPC 德国 German Collegiate Programming Contest——M. Mex Hex(使用区间维护可行域)

题目大意

题目大意
有一只魔法山猫会对你进行 nn 次攻击,每次攻击有一个潜能值 pip_i。你最终受到的痛苦值为所有未被防御的攻击潜能值的 mex\text{mex}(即未出现在这些潜能值集合中的最小自然数)。

你拥有一个盾牌,激活后可以持续防御接下来的 dd 次攻击。盾牌失效后会强制进入冷却,冷却时间同样持续 dd 次攻击,冷却期间无法再次激活。你可以多次激活盾牌,最早可以在第 1 次攻击前激活。

请制定最佳防御策略,使得受到的痛苦值最小。

输入格式
第一行包含两个整数 nndd (1n1051 \le n \le 10^5, 1dn1 \le d \le n),分别表示攻击总数和盾牌持续/冷却时间。
第二行包含 nn 个整数 p1,,pnp_1, \dots, p_n (0pi<n0 \le p_i < n),表示每次攻击的潜能值。

输出格式
输出一个整数,表示可以达到的最小痛苦值。

样例 1

1
2
5 1
0 1 0 1 0
1
0

解释d=1d=1。可以在第 1、3、5 次攻击时激活盾牌(防御区间为 [1,1],[3,3],[5,5][1,1], [3,3], [5,5]),冷却区间为 [2,2],[4,4][2,2], [4,4]。此时只有潜能为 1 的攻击击中你,mex({1,1})=0\text{mex}(\{1, 1\}) = 0

样例 2

1
2
5 2
0 1 0 1 0
1
2

解释d=2d=2。无论如何安排,冷却期间总会漏掉潜能为 0 和 1 的攻击。击中你的集合包含 {0,1}\{0, 1\}mex2\text{mex} \ge 2

样例 3

1
2
14 2
8 1 1 0 0 2 0 1 2 1 0 6 4 2
1
2

image

可以像图上一样防御。

样例 4

1
2
4 2
0 1 2 0
1
1

解释:最早只能在第 1 次攻击前激活,无法同时防御住第 1 个和第 4 个潜能为 0 的攻击(因为防御完第 1 个后,盾牌在第 2、3 个攻击期间防御,第 4 个攻击时可能刚结束或者在冷却,取决于具体激活时机,但无法跨越防御第 1 个和第 4 个而不防御中间的)。选择防御第 2、3 个攻击,漏掉第 1、4 个攻击(潜能均为 0),mex({0,0})=1\text{mex}(\{0, 0\}) = 1

思路讲解

这道题目其实不是很难,其实就是维护一下可行域就可以了。

那么初始的可行域是从一,就是初始的可行域的左起点是 Max 一和 P 减 d 加一右起点就是 P。然后在后面的过程当中,我们去不断的更新左起点。一旦当左起点它比右起点还大的时候,左起点比右起点大的时候呢,这个时候就说明我们需要新开一个区间了。然后我们新开区间的左起点,就是可行域的左端点,就是 L 加2乘以 d 和 P 减 d 加一中取一个 max 如果说然后这个可行域的 R 就是 P 。如果说 L 大于 R 那就说明就进不了可行域。就这个是错的,就 return false 就好了。

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
// 哦,这道题目的难点就在于这个 check 函数
// 负责检查该 VAL 是否可以被我们的防御魔法所全部覆盖
auto check=[&](ll val) -> bool {
if (g[val].empty()) {
return true;
}
// 维护一段可行起点区间
// 下面是起始的可行域。
ll l=max(1ll,g[val][0]-D+1),r=g[val][0];
for (int i=1;i<g[val].size();++i) {
ll p=g[val][i];
ll to_l=max(l,p-D+1);
// 我们先要检查一下这个 L 啊,这个 L 如果超过这个 R 了以后,我们就需要新建一段区间了。
if (to_l>r) {
l=max(l+2*D,p-D+1);
r=p;
if (l>r) {
return false;
}
}else {
l=to_l;
}
}
return true;
};

AC代码

AC
https://qoj.ac/submission/2032675
AC
https://codeforces.com/gym/106129/submission/362772763

心路历程(WA,TLE,MLE……)

这个 check 函数是负责什么呢?是负责检查该 VAL 是否可以被我们的防御魔法所全部覆盖。直接采用这个贪心啊,它其实是没那么简单的。因为虽然说第一个我们肯定是要覆盖的,但是呢我们并不一定要在第一个位置就启用这个防御魔法,我们可以在其更前面的位置启用这个防御魔法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 哦,这道题目的难点就在于这个 check 函数
auto check=[&](ll val) {
ll st=-INF;
for (auto p:g[val]) {
if (p-st+1<=D) {
continue;
}
if (p-st+1<=2*D) {
return false;
}
st=p;
}
return true;
};