题意:$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]$相互交叉的部分则可以用差分、前缀和来处理,记录每个点的覆盖情况,计算被覆盖区间的总长度。
#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 ", l); return 0; }