题目链接:https://codeforces.com/problemset/problem/1136/E
题意:
给出一个 $a[1 sim n]$,以及一个 $k[1 sim (n-1)]$,初始保证所有的 $1 le i le n-1$ 都满足 $a[i]+k[i] le a[i+1]$。
现在有两种操作:
第一种是令指定的 $a[i]$ 加上一个非负整数 $x$,此时若有 $a[i] + k[i] > a[i+1]$,则 $a[i+1]$ 变为 $a[i] + k[i]$,往后依次类推。
第二种是给定一个区间 $[l,r]$ 求 $a[l] + cdots + a[r]$。
题解:
首先,我们知道对一个 $a[i]$ 加上 $x$ 后,根据其后面的 $a[i] + k[i] le a[i+1], a[i+1] + k[i+1] le a[i+2], cdots$ 的“松紧程度”的变化,$a[i]$ 加上 $x$ 其带来的影响会逐步减弱,直到在某个位置 $j$ 之后完全消失,这个 $a[j]$ 是最后一个要被修改的数。
那么,如果我们找到了这个区间 $[i,j]$,我们现在要做的修改操作,就是要对这个区间进行一定的修改。
不难发现,这个区间内的第一个数变成了 $a[i]+x$,第二个变成了 $a[i]+x+k[i]$,第三个变成了 $a[i]+x+k[i]+k[i+1]$,依次类推……
考虑这个式子可以分为两部分:$a[i] + x$ 部分,以及 $k[i] + k[i+1] + cdots$ 部分。
可以考虑分开维护这两个部分,前一部分很好维护,线段树区间更新;后一部分直接维护比较困难,我们可以这样维护:
$k[i],k[i]+k[i+1],k[i]+k[i+1]+k[i+2],cdots$,不难看出是一个类似于 $k$ 数组的前缀和的求和,举个栗子:
$k_3 ,: k_3+k_4 ,: k_3+k_4+k_5 ,: k_3+k_4+k_5+k_6$,如果是前缀和求和,那么应当是 $k_1+k_2+k_3 :,: k_1+k_2+k_3+k_4 :,: k_1+k_2+k_3+k_4+k_5 :,: k_1+k_2+k_3+k_4+k_5+k_6$。
也就是说,要在前缀和上减掉 $4 imes (k_1+k_2)$,不难发现,这个值是比较好维护的,是对某一段区间直接赋值,所以可以用线段树维护这个东西。
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll INF=1e18; const int maxn=1e5+10; int n,q; ll a[maxn],k[maxn],s[maxn]; struct Node { int l,r; ll val,lazy; void update(ll x) { val=(r-l+1)*x; lazy=x; } }; struct SegmentTree { #define ls (rt<<1) #define rs (rt<<1|1) Node o[maxn<<2]; void pushdown(int rt) { if(o[rt].lazy!=-INF) { o[ls].update(o[rt].lazy); o[rs].update(o[rt].lazy); o[rt].lazy=-INF; } } void pushup(int rt) { o[rt].val=o[ls].val+o[rs].val; } void build(int rt,int l,int r,ll v[]) { o[rt].l=l, o[rt].r=r; o[rt].lazy=-INF; if(l==r) { o[rt].val=v[l]; return; } int mid=(l+r)>>1; build(ls,l,mid,v); build(rs,mid+1,r,v); pushup(rt); } void update(int rt,int st,int ed,ll val) { if(st<=o[rt].l && o[rt].r<=ed) { o[rt].update(val); return; } pushdown(rt); int mid=(o[rt].l+o[rt].r)>>1; if(st<=mid) update(ls,st,ed,val); if(mid<ed) update(rs,st,ed,val); pushup(rt); } ll query(int rt,int st,int ed) { if(st<=o[rt].l && o[rt].r<=ed) return o[rt].val; pushdown(rt); ll res=0; int mid=(o[rt].l+o[rt].r)>>1; if(st<=mid) res+=query(ls,st,ed); if(mid<ed) res+=query(rs,st,ed); return res; } }T[2]; inline ll getval(int p) { return T[0].query(1,p,p)+k[p]-T[1].query(1,p,p); } void modify(int p,ll x) { int l=p, r=n; ll base=getval(p); while(l<r) { int mid=(l+r+1)>>1; if(base+x+k[mid]-k[p]>getval(mid)) l=mid; else r=mid-1; } T[0].update(1,p,r,base+x); T[1].update(1,p,r,k[p]); } int main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; k[1]=s[1]=0; for(int i=2;i<=n;i++) cin>>k[i], k[i]+=k[i-1], s[i]=s[i-1]+k[i]; T[0].build(1,1,n,a); T[1].build(1,1,n,k); cin>>q; char op[2]; while(q--) { cin>>op; if(op[0]=='+') { int p; ll x; cin>>p>>x; modify(p,x); } if(op[0]=='s') { int l,r; cin>>l>>r; ll res1=T[0].query(1,l,r); ll res2=s[r]-s[l-1]-T[1].query(1,l,r); cout<<res1+res2<<' '; } } }
有一个注意点是懒标记初始化成负无穷。