原题见(https://www.luogu.org/problem/show?pid=1083)
这道题我第一眼看见果断是线段树啊,但是NOIP第二天第二题考线段树岂不是药丸,但是好来才知道正解居然是二分答案,如果是考场上根本想不到啊,二分的话首先要满足单调的,我们知道订单的顺序肯定是单调的,这样子把订单二分就行了,更简单一点的就是NOIp2015的跳石头,如果还有没有思路的可以去看看这个~飞机票(http://blog.csdn.net/a1351937368/article/details/76473342)其实说到这里就应该有点思路了吧(贴下代码,基本上重要的注释都在里面写出来了):
//二分
#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<stack>
#include<cmath>
using namespace std;
long long n,m,room[1001010],f[1010010],r[1001010];
struct node{
int num,s,e;
}a[1001000];//num表示第i个订单需要教室个数,
//s(tart)为开始的日子,e(nd)为结束的日子
inline int read(){
int num;
char ch;
while((ch=getchar())<'0' || ch>'9');
num=ch-'0';
while((ch=getchar())>='0' && ch<='9'){
num=num*10+ch-'0';
}
return num;
}
inline void out(int x){
if(x>=10){
out(x/10);
}
putchar(x%10+'0');
}
inline bool deal(int mid){
//判断到第mid个订单是否会出现教室不够借的现象
memset(f,0,sizeof(f)),memset(r,0,sizeof(r));//初始化数组
for(register int i=1;i<=mid;i++){//一到mid的订单逐个枚举
f[a[i].s]-=a[i].num,f[a[i].e+1]+=a[i].num;//当前开始的减去当前订单需要的,结束的加上需要的
//用前缀和修改区间值
}
for(register int i=1;i<=n;i++){
f[i]+=f[i-1];//一维前缀和
if(room[i]+f[i]<0){//room[i]为第i天的教室个数,如果第i天需要的教室数加上拥有的教室数小于0
return true;//当前节点会出现教室不够用的情况
}
}
return false;//当前节点不会出现教室不够用的情况
}
int main(){
n=read(),m=read();//天数和订单数量
for(register int i=1;i<=n;i++){
room[i]=read();//第i天可用于租借的教室数量
}
for(register int i=1;i<=m;i++){
a[i].num=read(),a[i].s=read(),a[i].e=read();//租借数量,租借开始时间,租借结束时间
}
int l=1,r=m,mid;//二分开始
while(l<r){
mid=(l+r)/2;
if(deal(mid)){//如果mid不满足
r=mid;//如果当前的mid值不符合那么将右节点左移
}
else{
l=mid+1;//否则左节点右移
}
}
if(r==m){//说明没有出现教室不够用的情况
out(0);
}
else{
printf("-1 \n"),out(r);
}
return 0;
}
再加点玄学优化这道题可以说跑的很快20个点2256ms就可以跑完了