题面
分析
由于本题涉及区间,我们可以将天数看做数组的下标,每一个请求都是在对一个区间的值进行修改,于是可以将本题看做一个区间修改的题目,而支持区间修改的数据结构无非就线段树和树状数组
这里使用线段树的做法
维护区间最小值,当最小值为负时返回false,输出申请人编号
code
#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(int i=start;i<=end;i++)
#define clean(arry,num); memset(arry,num,sizeof(arry));
#define min(a,b) ((a>b)?b:a)
#define max(a,b) ((a<b)?b:a)
#define ll long long
ll n,m;
const int maxn=1000000+10,maxm=1000000+10;
ll data[maxn];
ll minn[maxn<<2],lazy[maxn<<2];
inline ll read()
{
ll ans=0;char r=getchar();bool neg=false;
while(r<'0'||r>'9'){if(r=='-')neg=true;r=getchar();}
while(r>='0'&&r<='9'){ans=ans*10+r-'0';r=getchar();}
return (neg)?-ans:ans;
}
void pushup(ll rt)
{
minn[rt]=min(minn[rt<<1],minn[rt<<1|1]);
}
void buildtree(ll nl,ll nr,ll rt)
{
lazy[rt]=0;
if(nl==nr)
{
minn[rt]=data[nl];
return;
}
int m=nl+(nr-nl)/2;
buildtree(nl,m,rt<<1);
buildtree(m+1,nr,rt<<1|1);
pushup(rt);
}
void pushdown(ll rt,ll len)
{
if(!lazy[rt])return;
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
minn[rt<<1]-=lazy[rt];
minn[rt<<1|1]-=lazy[rt];
lazy[rt]=0;
}
bool add(ll l,ll r,ll nl,ll nr,ll rt,ll num)
{
if(l<=nl&&nr<=r)
{
minn[rt]-=num;
if(minn[rt]<0)return false;
lazy[rt]+=num;
return true;
}
pushdown(rt,nr-nl+1);
ll m=nl+(nr-nl)/2;
bool suc=1;
if(m>=l)suc&=add(l,r,nl,m,rt<<1,num);
if(m<r)suc&=add(l,r,m+1,nr,rt<<1|1,num);//位运算真好用哈哈哈
pushup(rt);
return (suc)?true:false;
}
int main()
{
//freopen("datain.txt","r",stdin);
n=read();m=read();
loop(i,1,n)data[i]=read();
buildtree(1,n,1);
loop(i,1,m)
{
ll d,s,t;
d=read();s=read();t=read();
if(!add(s,t,1,n,1,d))
{
printf("-1\n%d",i);
exit(0);
}
}
printf("0");
return 0;
}
/********************************************************************
ID:Andrew_82
LANG:C++
PROG:S_tree
********************************************************************/
学到的东西
- 线段树的各种标记维护
从代码上看,线段树的核心数据其实存储在标记数组中
(如sum[maxn<<2],minn[maxn<<2])
而原数组基本没有改动,原数组的作用仅在建树时体现(因此完全可以写一个类出来~~~~~)
标记数组一般有:sum,minn,maxx(区间乘法?蒟蒻表示不会)
而每次对区间的修改其实并不发生在原数组上,只发生在标记数组上,因此在维护时,应该根据其特点来构造pushup和pushdown函数,且只对于标记数组生效