牛客 监视任务 nc14136

题目链接
𝑅𝑒𝑘𝑖在课余会接受一些民间的鹰眼类委托,即远距离的狙击监视防卫。
𝑅𝑒𝑘𝑖一共接到了𝑚份委托,这些委托与𝑛个直线排布的监视点相关。
第𝑖份委托的内容为:对于区间[𝑙𝑖, 𝑟𝑖]中的监视点,至少要防卫其中的𝑘𝑖个。
𝑅𝑒𝑘𝑖必须完成全部委托,并且希望选取尽量少的监视点来防卫。

输入描述:
第一行,两个正整数𝑛,𝑚。
接下来𝑚行,每行三个整数𝑙𝑖,𝑟𝑖,𝑘𝑖。
输出描述:
一行,一个整数,即所需防卫的最少监视点数量。

题解:贪心和线段树,我们把所有委托根据其右边界的大小从小到大排序排序,然后遍历这些委托,但遍历到某个委托时查询该委托区间已有的监视点,看看还缺多少,然后将缺的全部尽可能的补在靠右的位置,为什么了,因为我们根据其右边界大小从小到大枚举这些委托,你现在所处理的委托的右边界一定大于等于前一个的右边界,而前一个委托的要求已经达到,你将监视点补在前段几乎没有作用,而补在后端,因为下一个委托的右边界一定大于等于该委托的右边界,所以补在右边才能使这些监视点的作用尽可能的被下一个委托所利用

代码如下:
线段树做法

#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;
}

树状数组做法:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 500000 + 10;
const int INF = 1e9 + 7;
int tree[maxn], n, m;
struct node
{
    int l, r, w;
    node(){}
    bool operator < (const node& a)const
    {
        return r < a.r;
    }
}a[maxn << 1];
int lowbit(int x)
{
    return (x & -x);
}
int sum(int x)
{
    int ans = 0;
    while(x)
    {
        ans += tree[x];
        x -= lowbit(x);
    }
    return ans;
}
bool vis[maxn];
void add(int x, int d)
{
    while(x <= n)
    {
        tree[x] += d;
        x += lowbit(x);
    }
}
inline int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    } while('0' <= ch && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    } return x * f;
}
int main()
{
    n = read(), m = read();
    for(int i = 1; i <= m; i++)
        a[i].l = read(), a[i].r = read(), a[i].w = read();
    sort(a + 1, a + 1 + m);
    for(int i = 1; i <= m; i++)
    {
        int tmp = sum(a[i].r) - sum(a[i].l - 1);
        for(int j = a[i].r; j >= a[i].l && tmp < a[i].w; j--)
            if(!vis[j])vis[j] = 1, add(j, 1), tmp++;
    }
    printf("%d\n", sum(n));
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值