思路
两种操作:区间减和区间查询。
最初考虑用线段树维护区间和以及区间最小值。对于每次区间减,如果其中的一个区间最小值大于,那就直接对该区间减去,并打上懒标记,否则的话就单点修改。超时。
优化:如果一个元素被减为,就可以直接把它忽略,因为并不影响对包含的区间实施区间减操作的效果。维护,代表每个区间的非零叶子数目。如果一个元素被减为,则将该元素的值变为,即在维护其祖先区间最小值时将该点忽略。这样就可以做到对一个区间,所有的值要么大于,要么为,可以在深度较浅处直接修改并返回。
代码
#include<bits/stdc++.h>
#define rep(i,st,ed) for(ll i=st;i<=ed;++i)
#define lc p<<1
#define rc p<<1|1
using namespace std;
typedef long long ll;
ll n,q;
const ll N=1E5+10,INF=0x3f3f3f3f;
ll a[N],t[N<<2],mi[N<<2],ct[N<<2],tag[N<<2];
void update(ll p)
{
t[p]=t[lc]+t[rc];
mi[p]=min(mi[lc],mi[rc]);
ct[p]=ct[lc]+ct[rc];
}
void build(ll p,ll l,ll r)
{
if(l==r)
{
t[p]=a[l];
mi[p]=a[l];
ct[p]=1;
return;
}
ll mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
update(p);
}
void flag(ll p,ll val)
{
t[p]-=ct[p]*val;
mi[p]-=val;
tag[p]-=val;
}
void push_down(ll p,ll l,ll r)
{
if(!tag[p])
return;
ll mid=(l+r)>>1;
flag(lc,-tag[p]);
flag(rc,-tag[p]);
tag[p]=0;
}
void add(ll p,ll l,ll r,ll al,ll ar,ll val)
{
if(!ct[p])
return;
if(l==r)
{
t[p]=max(0ll,t[p]-val);
mi[p]=t[p];
if(t[p]==0)
{
mi[p]=INF;
ct[p]=0;
}
return;
}
if(mi[p]>=val && al<=l && ar>=r)
{
t[p]-=ct[p]*val;
mi[p]-=val;
tag[p]-=val;
return;
}
push_down(p,l,r);
ll mid=(l+r)>>1;
if(al<=mid)
add(lc,l,mid,al,ar,val);
if(ar>mid)
add(rc,mid+1,r,al,ar,val);
update(p);
}
ll query(ll p,ll l,ll r,ll al,ll ar)
{
ll ret=0;
if(al<=l && ar>=r)
return t[p];
push_down(p,l,r);
ll mid=(l+r)>>1;
if(al<=mid)
ret+=query(lc,l,mid,al,ar);
if(ar>mid)
ret+=query(rc,mid+1,r,al,ar);
return ret;
}
int main()
{
scanf("%lld%lld",&n,&q);
rep(i,1,n)
scanf("%lld",a+i);
build(1,1,n);
while(q--)
{
ll op,l,r;
ll val;
scanf("%lld",&op);
scanf("%lld %lld",&l,&r);
if(op==1)
{
if(l<=r)
printf("%lld\n",query(1,1,n,l,r));
else
printf("%lld\n",query(1,1,n,l,n)+query(1,1,n,1,r));
}
else
{
scanf("%lld",&val);
if(l<=r)
add(1,1,n,l,r,val);
else
{
add(1,1,n,1,r,val);
add(1,1,n,l,n,val);
}
}
}
}