Address
https://www.lydsy.com/JudgeOnline/problem.php?id=2811
Solution
先假设忍者的数目不一定为恰好
个。
考虑先把不可能有忍者(形如
的报告)的区间删掉。
然后求出至少有多少个忍者。
这是一个经典贪心:一些区间,选出最少的点,使每个区间包含至少一个点。
先将包含有其他区间的区间删掉,然后把区间按照左端点递增排序(由于包含有其他区间的区间已经被删掉,所以左端点排序后右端点也是递增的)。
如果至少有
个忍者则直接输出这些忍者的位置。
我们可以求出:
表示前
个区间至少有多少个忍者;
表示从
个区间到最后一个区间至少有多少个忍者。
上面的那个经典贪心做完之后,我们会得出一组方案,满足所有给定区间里至少有一个忍者(但总数不一定恰好为
),且得出这组方案中每个忍者的位置。
显然,如果一个位置
一定有忍者,那么
一定是上面的算法得出的方案中忍者的位置之一。
由贪心的过程易得
一定是某一区间的末尾,并且如果
位置
放忍者时,总忍者数一定超过
一定有忍者。
我们可以利用二分查找,得出:
右端点小于
的最后一个区间,为第
个区间;
左端点大于
的第一个区间,为第
个区间。
如果
,那么位置
一定有忍者。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5;
int n, K, tm, m, T, cnt[N], f[N], g[N], id[N], rev[N], le[N], ri[N], tot;
struct cyx {int l, r;} q[N], stk[N];
bool comp(cyx a, cyx b) {return a.l < b.l || (a.l == b.l && a.r > b.r);}
int main() {
int i, a, b, c, pos = 0; bool flag = 0;
n = read(); K = read(); tm = read(); For (i, 1, tm) {
a = read(); b = read(); c = read();
if (!c) cnt[a]++, cnt[b + 1]--; else q[++m] = (cyx) {a, b};
}
For (i, 1, n) cnt[i] += cnt[i - 1]; For (i, 1, n) if (!cnt[i])
rev[id[++T] = i] = T; if (T == K) {
For (i, 1, T) printf("%d\n", id[i]); return 0;
}
For (i, 1, n) {if (!cnt[i]) pos = i; le[i] = pos;}
pos = n + 1; Rof (i, n, 1) {if (!cnt[i]) pos = i; ri[i] = pos;}
For (i, 1, m) q[i].l = rev[ri[q[i].l]], q[i].r = rev[le[q[i].r]];
sort(q + 1, q + m + 1, comp); For (i, 1, m) {
while (tot && q[i].r <= stk[tot].r) tot--; stk[++tot] = q[i];
}
int orzcyx = 0, orzpyz = T + 1; For (i, 1, tot)
f[i] = stk[i].l > orzcyx ? (orzcyx = stk[i].r, f[i - 1] + 1) : f[i - 1];
Rof (i, tot, 1)
g[i] = stk[i].r < orzpyz ? (orzpyz = stk[i].l, g[i + 1] + 1) : g[i + 1];
For (i, 1, tot) {
if (f[i] == f[i - 1]) continue; if (stk[i].l == stk[i].r) {
printf("%d\n", (flag = 1, id[stk[i].r])); continue;
}
int x = stk[i].r - 1, L = 1, R = tot; while (L <= R) {
int mid = L + R >> 1; if (stk[mid].r < x) L = mid + 1;
else R = mid - 1;
}
int tmp = R; L = 1; R = tot; while (L <= R) {
int mid = L + R >> 1; if (stk[mid].l > x) R = mid - 1;
else L = mid + 1;
}
if (f[tmp] + g[L] + 1 > K) printf("%d\n", (flag = 1, id[stk[i].r]));
}
if (!flag) puts("-1"); return 0;
}