来源:牛客网
:
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
𝑅𝑒𝑘𝑖在课余会接受一些民间的鹰眼类委托,即远距离的狙击监视防卫。
𝑅𝑒𝑘𝑖一共接到了𝑚份委托,这些委托与𝑛个直线排布的监视点相关。 第𝑖份委托的内容为:对于区间[𝑙𝑖,
𝑟𝑖]中的监视点,至少要防卫其中的𝑘𝑖个。 𝑅𝑒𝑘𝑖必须完成全部委托,并且希望选取尽量少的监视点来防卫。
输入描述:
第一行,两个正整数𝑛,𝑚。
接下来𝑚行,每行三个整数𝑙𝑖,𝑟𝑖,𝑘𝑖。
输出描述:
一行,一个整数,即所需防卫的最少监视点数量。
示例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,𝑙𝑖 ≤ 𝑟𝑖,𝑘𝑖 ≤ 𝑟𝑖−𝑙𝑖+1。
题解:
贪心+线段树
我们先将给的m个区间进行排序,按照右端点值从小到大排
然后对每个区间值查询监视点的数量,如果不满足要求就填充,记得要尽可能的给区间的右侧填充,因为这样更有利于后面的区间能用上
比如[1,3]和[3,6],两个区间各需要一个监视点,我们我们给[1,3]按上一个,尽可能在右侧安装,也就是点3,这样查询[3,6]时我们发现已经有了,就不用给[3,6]再安装了
区间修改操作涉及到了线段树
那在线段树区间修改的时候,怎么能先优先更新右侧?
很简单,我们在更新是优先更新右节点
if(r>mid)val=update(id*2+1,mid+1,R,l,r,val);if(l<=mid)val=update(id*2,L,mid,l,r,val);
代码中是先更新id*2+1即右节点,这样就会优先在右侧填充,如果两个语句反过来,就是优先填充左侧
有两种写法(一种用到pushdown的区间修改,另一种单纯只用到单点修改)对了,每个坐标点只能放置一个监视点。。
代码:
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<string>
#include<time.h>
#include<math.h>
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<functional>
using namespace std;
#define ll long long
#define inf 1000000000
#define mod 1000000007
#define maxn 500005
#define lowbit(x) (x&-x)
#define eps 1e-9
struct node
{int l,r,k;
}a[maxn*2];
int n,m,sum[maxn*5],lazy[maxn*5];
bool comp(node a,node b)
{return a.r<b.r;
}
void pushdown(int l,int r,int id)
{if(lazy[id]){int mid=(l+r)/2;lazy[id*2]=1;sum[id*2]=mid-l+1;lazy[id*2+1]=1;sum[id*2+1]=r-mid;lazy[id]=0;}
}
int update(int id,int L,int R,int l,int r,int val)
{if(val<=0)return 0;if(L>=l && R<=r){if(R-L+1-sum[id]<=val)//需要布置监视点 (即便当前全部布满也不能达到要求){val-=R-L+1-sum[id];//还需要的监视点 lazy[id]=1;sum[id]=R-L+1;//全部布满return val;//还需要布满的数量 }}pushdown(L,R,id);int mid=(L+R)/2;if(r>mid)val=update(id*2+1,mid+1,R,l,r,val);if(l<=mid)val=update(id*2,L,mid,l,r,val);sum[id]=sum[id*2]+sum[id*2+1];return val;
}
int query(int id,int L,int R,int l,int r)
{if(L>=l && R<=r)return sum[id];pushdown(L,R,id);int mid=(L+R)/2,res=0;if(l<=mid)res+=query(id*2,L,mid,l,r);if(r>mid)res+=query(id*2+1,mid+1,R,l,r);return res;
}
int main(void)
{int i;scanf("%d%d",&n,&m);for(i=1;i<=m;i++)scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].k);sort(a+1,a+m+1,comp);for(i=1;i<=m;i++){int w=query(1,1,n,a[i].l,a[i].r);int w1=query(1,1,n,6,6);printf("%d~%d w=%d\n",a[i].l,a[i].r,w);//printf("%d~%d w=%d\n",6,6,w1);update(1,1,n,a[i].l,a[i].r,a[i].k-w);}printf("%d\n",query(1,1,n,1,n));return 0;
}
// 监视任务 运行/限制:898ms/2000ms
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
struct node {int l, r, k;bool operator<(const node& b)const {return r < b.r;}
}a[1000005];
int sum[500005 << 2];
int book[500005];
void pushUp(int root) {sum[root] = sum[root << 1] + sum[root << 1 | 1];
}
void build(int le, int rig, int root) {if (le == rig) {sum[root] = 0;return;}int mid = (le + rig) >> 1;build(le,mid,root<<1);build(mid+1,rig,root<<1|1);pushUp(root);
}
int query(int l, int r, int le, int rig, int root) {if (l <= le && r >= rig) {return sum[root];}int re = 0;int mid = (le + rig) >> 1;if (l <= mid) {re += query(l, r, le,mid,root<<1);}if (r > mid) {re += query(l, r, mid+1,rig,root<<1|1);}return re;
}
void update(int pos, int le, int rig, int root) {if (le == rig) {sum[root] = 1;return;}int mid = (le + rig) >> 1;if (pos <= mid) {update(pos, le,mid,root<<1);}else {update(pos, mid+1,rig,root<<1|1);}pushUp(root);
}
int main(){int n, m, ans;while (scanf("%d%d", &n, &m) != EOF) {ans = 0;memset(book, 0, sizeof(book));for (int i = 0; i < m; i++) {scanf("%d%d%d",&a[i].l, &a[i].r, &a[i].k);}build(1, n, 1);sort(a, a + m);for (int i = 0; i < m; i++) {int pos = a[i].r;//区间右边界int cnt = query(a[i].l,a[i].r, 1, n, 1);//查询该区间已经被防卫的点的数量while (cnt < a[i].k) {while (book[pos]) pos--;//从右向左找位置加防卫book[pos] = 1;update(pos, 1, n, 1);ans++;cnt++;}}printf("%d\n", ans);}return 0;
}