牛客练习赛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;
}

 

posted @ 2018-02-03 17:35  Fighting_Heart  阅读(378)  评论(0编辑  收藏  举报