牛客练习赛3 F - 监视任务
链接:https://www.nowcoder.net/acm/contest/13/F
来源:牛客网
题目描述
𝑅𝑒𝑘𝑖在课余会接受一些民间的鹰眼类委托,即远距离的狙击监视防卫。
𝑅𝑒𝑘𝑖一共接到了𝑚份委托,这些委托与𝑛个直线排布的监视点相关。
第𝑖份委托的内容为:对于区间[𝑙𝑖, 𝑟𝑖]中的监视点,至少要防卫其中的𝑘𝑖个。
𝑅𝑒𝑘𝑖必须完成全部委托,并且希望选取尽量少的监视点来防卫。
𝑅𝑒𝑘𝑖一共接到了𝑚份委托,这些委托与𝑛个直线排布的监视点相关。
第𝑖份委托的内容为:对于区间[𝑙𝑖, 𝑟𝑖]中的监视点,至少要防卫其中的𝑘𝑖个。
𝑅𝑒𝑘𝑖必须完成全部委托,并且希望选取尽量少的监视点来防卫。
输入描述:
第一行,两个正整数𝑛,𝑚。
接下来𝑚行,每行三个整数
输出描述:
一行,一个整数,即所需防卫的最少监视点数量。
示例1
输入
11 5 3 7 3 8 10 3 6 8 1 1 3 1 10 11 1
输出
6
备注:
对于10%的数据,𝑛 ≤ 10。
对于20%的数据,𝑛 ≤ 20。
对于30%的数据,𝑛,𝑚 ≤ 30。
对于60%的数据,𝑛,𝑚 ≤ 1000。
对于100%的数据,𝑛 ≤ 500000,𝑚 ≤ 1000000,
题解
贪心,线段树优化。
区间按$R$从小到大排序,一个一个考察。如果发现某个区间内$1$的个数不满足要求,那么靠后放$1$。所有操作扔进线段树即可。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; int s[maxn * 4]; int f[maxn * 4]; int n, m; vector<int> g[maxn]; struct X { int L, R, k; }op[maxn]; void pushDown(int rt) { if(f[rt] == 0) return; s[2 * rt] = 0; f[2 * rt] = 1; s[2 * rt + 1] = 0; f[2 * rt + 1] = 1; f[rt] = 0; } void pushUp(int rt) { s[rt] = s[2 * rt] + s[2 * rt + 1]; } // 区间 [L, R] 中 0 的个数 int sum(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) { return s[rt]; } int mid = (l + r) / 2; int left = 0; int right = 0; pushDown(rt); if(L <= mid) left = sum(L, R, l, mid, 2 * rt); if(R > mid) right = sum(L, R, mid + 1, r, 2 * rt + 1); pushUp(rt); return left + right; } // 区间 [L, R] 覆盖为 1 void update(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) { s[rt] = 0; f[rt] = 1; return; } int mid = (l + r) / 2; pushDown(rt); if(L <= mid) update(L, R, l, mid, 2 * rt); if(R > mid) update(L, R, mid + 1, r, 2 * rt + 1); pushUp(rt); } void build(int l, int r, int rt) { s[rt] = r - l + 1; if(l == r) return; int mid = (l + r) / 2; build(l, mid, 2 * rt); build(mid + 1, r, 2 * rt + 1); } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= m; i ++) { scanf("%d%d%d", &op[i].L, &op[i].R, &op[i].k); g[op[i].R].push_back(i); } int ans = 0; build(1, n, 1); for(int i = 0; i <= 500000; i ++) { int sz = g[i].size(); for(int j = 0; j < sz; j ++) { int id = g[i][j]; int len = op[id].R - op[id].L + 1; int Sum = sum(op[id].L, op[id].R, 1, n, 1); if(len - Sum >= op[id].k) { continue; } int pos = -1; int left = op[id].L, right = op[id].R; while(left <= right) { int mid = (left + right) / 2; if(sum(mid, op[id].R, 1, n, 1) >= op[id].k - (len - Sum)) { pos = mid; left = mid + 1; } else { right = mid - 1; } } update(pos, op[id].R, 1, n, 1); ans = ans + op[id].k - (len - Sum); } } printf("%d\n", ans); return 0; }