題目連接:https://codeforces.com/problemset/problem/1136/Eios
題意:c++
給出一個 $a[1 \sim n]$,以及一個 $k[1 \sim (n-1)]$,初始保證全部的 $1 \le i \le n-1$ 都知足 $a[i]+k[i] \le a[i+1]$。數組
如今有兩種操做:ui
第一種是令指定的 $a[i]$ 加上一個非負整數 $x$,此時如有 $a[i] + k[i] > a[i+1]$,則 $a[i+1]$ 變爲 $a[i] + k[i]$,日後依次類推。spa
第二種是給定一個區間 $[l,r]$ 求 $a[l] + \cdots + a[r]$。code
題解:blog
首先,咱們知道對一個 $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]$ 是最後一個要被修改的數。ci
那麼,若是咱們找到了這個區間 $[i,j]$,咱們如今要作的修改操做,就是要對這個區間進行必定的修改。get
不難發現,這個區間內的第一個數變成了 $a[i]+x$,第二個變成了 $a[i]+x+k[i]$,第三個變成了 $a[i]+x+k[i]+k[i+1]$,依次類推……it
考慮這個式子能夠分爲兩部分:$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 \times (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<<'\n'; } } }
有一個注意點是懶標記初始化成負無窮。