思路:对给定的n个元素建一颗线段树,维护每个线段节点中以区间中的第一个元素作为最大元素的递减序列和tr[rt].sum。具体操作就是我们每次单点更新的时候,维护一下区间最小值,然后对于一个线段节点,通过计算它左孩子和右孩子的贡献值来更新自己,tr[rt].sum=tr[rt<<].sum+calcu(mid+1,r,rt<<1|1,tr[rt<<1].mi),calcu函数表示该区间小于mi的数形成的递减数列的和,若该区间的左孩子的最小值小于mi,我们可以直接算出其右孩子的贡献,然后继续calcu其左孩子,否则,左孩子的贡献为0,calcu其右孩子。
代码:
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define PI acos(-1.0)
const ll maxn=1e5+9;
ll ans;
struct tree{
ll sum,mi;
}tr[maxn*4];
void pushup(ll rt){
tr[rt].mi=min(tr[rt<<1].mi,tr[rt<<1|1].mi);
}
ll calcu(ll l,ll r,ll rt,ll mi){
if(l==r){
return tr[rt].mi<mi?tr[rt].sum:0;
}
ll mid=(l+r)>>1,ans=0;
if(tr[rt<<1].mi<mi)ans+=tr[rt].sum-tr[rt<<1].sum+calcu(l,mid,rt<<1,mi);
else ans+=calcu(mid+1,r,rt<<1|1,mi);
return ans;
}
ll query(ll ql,ll qr,ll l,ll r,ll rt,ll w){
if(l>=ql&&r<=qr){
ans+=calcu(l,r,rt,w);
return tr[rt].mi;
}
ll mid=(l+r)>>1;
if(mid>=ql)w=min(w,query(ql,qr,l,mid,rt<<1,w));
if(mid<qr)w=min(w,query(ql,qr,mid+1,r,rt<<1|1,w));
return w;
}
void update(ll pos,ll l,ll r,ll rt,ll val){
if(l==r){
tr[rt].sum=val;
tr[rt].mi=val;
return;
}
ll mid=(l+r)>>1;
if(pos<=mid)update(pos,l,mid,rt<<1,val);
else update(pos,mid+1,r,rt<<1|1,val);
pushup(rt);
tr[rt].sum=tr[rt<<1].sum+calcu(mid+1,r,rt<<1|1,tr[rt<<1].mi);
}
int main(){
int i,j,k,n,t,q;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++){
int x;scanf("%d",&x);
update(i,1,n,1,x);
}
for(i=0;i<q;i++){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
ans=0;query(a,b,1,n,1,inf);
if(c!=0||d!=0)
update(c,1,n,1,d);
printf("%lld\n",ans);
}
}
}