题意:$m$个士兵,每个士兵都有一个灵敏度$a[i]$,起点为$0$,终点为$n + 1$,在路上有$k$个陷阱,每个陷阱有三个属性$l[i],r[i],d[i]$,$l[i]$表示陷阱的位置,如果你走到$r[i]$这个位置,则可以拆掉这个陷阱,$d[i]$与士兵的灵敏度对应,如果$d[i]>a[j]$,则这个士兵就会被困住,现在你可以挑选任意数量的士兵,你有两种方式移动方式:

  • 带着士兵移动,但士兵不能被陷阱困住,每秒只能移动一格,从$x$到$x+1$或者$x-1$
  • 一个人去拆除陷阱$($你不会被陷阱困住$)$,每秒移动一格,从$x$到$x+1$或者$x-1$

问你最多可以带多少士兵,在时间$t$内移动到终点

思路:二分答案或者二分可以挑选士兵的最小灵敏度都可以,每次贪心带灵敏度最高的士兵,关键是二分后如何计算移动的最小时间,显然当多个陷阱的$l[i],r[i]$有交叉时,应该一个人继续前进拆除陷阱,直到$l[i],r[i]$没有交叉时回到士兵的位置带着士兵前进,否则直接回到士兵的位置,直到再次遇到能困住士兵的陷阱,一直这样到终点,$l[i],r[i]$相互交叉的部分则可以用差分、前缀和来处理,记录每个点的覆盖情况,计算被覆盖区间的总长度。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;

const int N = 200010;

struct node {
int l, r, d;
};

int a[N], cnt[N];
int n, m, k, t;
node p[N];

bool cmp(int a, int b)
{
return a > b;
}

bool check(int mid)
{
int imin = a[mid], tt = 0;
for (int i = 1; i <= n + 1; i++) cnt[i] = 0;
for (int i = 1; i <= k; i++)
if (p[i].d > imin)
cnt[p[i].l]++, cnt[p[i].r + 1]--;
for (int i = 1; i <= n + 1; i++) cnt[i] += cnt[i - 1];
for (int i = 1; i <= n + 1; i++)
if (cnt[i] >= 1) tt++;
tt = 2 * tt + n + 1;
return tt > t;
}

int main()
{
scanf("%d%d%d%d", &m, &n, &k, &t);
for (int i = 1; i <= m; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= k; i++)
scanf("%d%d%d", &p[i].l, &p[i].r, &p[i].d);
sort(a + 1, a + m + 1, cmp);
int l = 0, r = m;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) r = mid - 1;
else l = mid;
}
printf("%d\n", l);
return 0;
}