传送门
这道题我想到2种解法:
- 线段树法 O((mlog n))
- 差分法 O((n+m))
先说较简单的线段树法
维护一个最小值线段树
每次操作对一段区间减去一个数
看第几次操作全部数中的最小值<0
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#define inf 100000000
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef long long ll;
const int N=1000000;
int n,m,a[N+10],add[4*N+10],minn[4*N+10],tl[4*N+10],tr[4*N+10];
void build(int k,int l,int r)
{
tl[k]=l,tr[k]=r;
if(l==r)
{
minn[k]=a[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
minn[k]=min(minn[k<<1],minn[k<<1|1]);
}
inline void spread(int k)
{
if(add[k]==0) return;
minn[k<<1]+=add[k];
minn[k<<1|1]+=add[k];
add[k<<1]+=add[k];
add[k<<1|1]+=add[k];
add[k]=0;
}
void ask_add(int k,int l,int r,int d)
{
if(l<=tl[k]&&tr[k]<=r)
{
add[k]+=d,minn[k]+=d;
return;
}
spread(k);
int mid=(tl[k]+tr[k])>>1;
if(l<=mid) ask_add(k<<1,l,r,d);
if(r>=mid+1) ask_add(k<<1|1,l,r,d);
minn[k]=min(minn[k<<1],minn[k<<1|1]);
}
int ask_minn()
{
return minn[1];
}
int main()
{
scanf("%d%d",&n,&m);
rep(i,1,n)
{
scanf("%d",a+i);
}
build(1,1,n);
rep(i,1,m)
{
int d,l,r;
scanf("%d%d%d",&d,&l,&r);
ask_add(1,l,r,-d);
if(ask_minn()<0)
{
printf("-1
%d
",i);
return 0;
}
}
printf("0
");
return 0;
}
差分法(重点!!!)
可以先求给定数列的差分数列(c_i)
差分性质:
-
给原数列一段区间 l,r 加一个数d
-
即(c_l+=d,c_{r+1}-=d) 然后求前缀和即可得原数列(c)为差分数列
则用差分法求到最终数列
然后往回删 到全部数都满足>0为止
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#define inf 100000000
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef long long ll;
const int N=1000000;
int n,m,sum=0,a[N+10],d[N+10],l[N+10],r[N+10],c[N+10];
int main()
{
scanf("%d%d",&n,&m);
rep(i,1,n)
{
scanf("%d",a+i);
c[i]=a[i]-a[i-1];
}
rep(i,1,m)
{
scanf("%d%d%d",d+i,l+i,r+i);
c[l[i]]-=d[i];
c[r[i]+1]+=d[i];
}
int j=m;
rep(i,1,n)
{
sum+=c[i];
while(sum<0)
{
c[l[j]]+=d[j];
c[r[j]+1]-=d[j];
if(l[j]<=i&&i<=r[j])
sum+=d[j];
j--;
}
}
if(j==m) printf("0
");
else printf("-1
%d
",j+1);
return 0;
}