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

 

转载于:https://www.cnblogs.com/zufezzt/p/8410159.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值