内容目录
Language:
线段树的成段更新
Description 对于数列 A1, A2, ... , AN. 你要进行2个操作:将一个区间的数同加上某个数,输出一段区间的和。 Input 第一行2个整数表示数列长度和操作次数. 1 ≤ N,Q ≤ 100000. Output 输出所有询问的答案,每行1个。 Sample Input 10 5 1 2 3 4 5 6 7 8 9 10 Q 4 4 Q 1 10 Q 2 4 C 3 6 3 Q 2 4 Sample Output 4 55 9 15 Hint
longint会爆的。
Source
POJ Monthly--2007.11.25, Yang Yi
|
显然这题是线段树。
但是我之前习惯的nowl和nowr似乎太费解了(以致于我自己都要想半天)
回去看看其它人咋写……
Program poj3468; const maxn=100000; maxq=100000; var n,m,i,j,k:longint; lazy,t:array[1..maxn*8] of int64; c:char; Procedure pushup(root:longint); begin t[root]:=t[root*2]+t[root*2+1]; end; Procedure build(l,r,root:longint); var m:longint; begin if (l=r) then begin read(t[root]); exit; end; m:=(l+r) shr 1; build(l,m,root*2); build(m+1,r,root*2+1); pushup(root); end; Procedure Pushdown(l,r,root:longint); var m:longint; begin m:=(l+r) shr 1; if (lazy[root]=0) then exit; inc(lazy[root shl 1],lazy[root]); inc(lazy[root shl 1+1],lazy[root]); inc(t[root shl 1],lazy[root]*(m-l+1)); inc(t[root shl 1+1],lazy[root]*(r-(m+1)+1)); lazy[root]:=0; end; Procedure update(l,r,nowl,nowr,root,cost:longint); var m:longint; begin if (l<=nowl) and (nowr<=r) then begin inc(lazy[root],cost); inc(t[root],(nowr-nowl+1)*cost); exit; end; pushdown(nowl,nowr,root); m:=(nowl+nowr) shr 1; if (l<=m) then update(l,r,nowl,m,root*2,cost); if (m+1<=r) then update(l,r,m+1,nowr,root*2+1,cost); pushup(root); end; function query(l,r,nowl,nowr,root:longint):int64; var m:longint; res:int64; begin if (l<=nowl) and (nowr<=r) then begin exit(t[root]); end; pushdown(nowl,nowr,root); m:=(nowl+nowr) shr 1; res:=0; if (l<=m) then res:=res+query(l,r,nowl,m,root*2); if (m+1<=r) then res:=res+query(l,r,m+1,nowr,root*2+1); exit(res); end; begin readln(n,m); fillchar(lazy,sizeof(lazy),0); build(1,n,1); readln; while (m>0) do begin read(c); if c='Q' then begin readln(i,j); writeln(query(i,j,1,n,1)); end else begin readln(i,j,k); update(i,j,1,n,1,k); end; dec(m); end; end.
接下来再来说说zkw线段树的解法:
这涉及到原数组A,差分数组A‘,差分数组前缀前缀和A'':
首先,它们3者满足如下关系:
1.差分数组的前缀和SA[i]'为原数组A[i](定义)。
2.原数组的前缀和SA[I]为差分数组的前缀前缀和A''[i];
接下来我们分别建立A‘和(i*A')的{单点修改,区间求和}线段树
显然要想知道
A[i]+A[i+1]+....A[ j ]
=SA[ j ]-SA[ i -1]
=A''[ j ] - A'' [ i-1 ]
=(SA'[1] +SA'[2]+...SA''[ j ] )- (SA'[1] +SA'[2]+...SA''[ i-1 ] )
={ j *A'[1] +(j-1)'A'[2]+....A'[ j ]}-{ (i-1) *A'[1] +(i-2)'A'[2]+....A'[ i-1 ] }
={ (j+1)*(A'[1]+A'[2]+...A'[ j ] )-(1*A'[1]+2*A'[2]+...j*A'[ j ] } - { i*(A'[1]+A'[2]+...A'[ i-1 ] )-(1*A'[1]+2*A'[2]+...(i-1)*A'[ i-1 ] }
这个方程如果看起来乱的话就直接看上面的图A''(相当于原数组前缀和SA-请想办法表示成A‘的累加形式)
显然如果将一个区间[A,B]+V,对3个数组影响如下:
幸运的是,我们只要维护A‘和{i*A’}
A和A‘’都要折腾一段区间,只有A'只要修改头和(尾+1)(如果存在)
那么{i*A‘} 的 维护 也只要修改2个节点 (请注意每个A‘ 实际上是独立的),同上:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<cctype> #include<iostream> #include<functional> #include<algorithm> using namespace std; #define MAXN (100000+10) #define MAXAi (1000000000) #define MAXCi (10000) __int64 a[MAXN]; int n,q; char c[10]; class SegMentTree { public: int M; __int64 t[MAXN*10]; void fillchar() { M=1;while (M-2<n) M<<=1; memset(t,0,sizeof(t)); } __int64 h(__int64 x,__int64 y) { return x+y; } void update(int x,__int64 c) { for (x>>=1;x;x>>=1) t[x]=t[x]+c; } void insert(int x,__int64 c) { x+=M; t[x]+=c; update(x,c); } __int64 find(int l,int r) { l=l-1+M;r=r+1+M; __int64 ans=0; while (l^r^1) { if (~l&1) {ans+=t[l+1];/* cout<<l+1<<' ';*/} if (r&1) {ans+=t[r-1];/* cout<<r-1<<' ';*/} l>>=1;r>>=1; } // cout<<ans<<' '; return ans; } /* void print() { for (int i=1;i<=M*2;i++) if (t[i]!=0) cout<<i<<':'<<t[i]<<' '; cout<<endl; } */ }t_ai,t_iai; int main() { // freopen("Poj3468.in","r",stdin); scanf("%d%d",&n,&q);t_ai.fillchar();t_iai.fillchar(); a[0]=0;for (int i=1;i<=n;i++) scanf("%I64d",&a[i]); for (int i=1;i<=n;i++) { t_ai.insert(i,a[i]-a[i-1]); t_iai.insert(i,i*(a[i]-a[i-1])); } for (int i=1;i<=q;i++) { scanf("%s",c); if (c[0]=='Q') { int p1,p2; scanf("%d%d",&p1,&p2);p1--; __int64 ans1=(p1>0)?(__int64)(t_ai.find(1,p1)*(__int64)((__int64)p1+1)-(__int64)t_iai.find(1,p1)):0;//cout<<ans1<<endl; __int64 ans2=(__int64)t_ai.find(1,p2)*(__int64)(p2+1)-(__int64)t_iai.find(1,p2);//cout<<ans2<<endl; cout<<(__int64)(ans2-ans1)<<endl; } else { int l,r; __int64 c; scanf("%d%d%I64d",&l,&r,&c); t_ai.insert(l,(__int64)c); t_iai.insert(l,(__int64)c*l); if (r<n) { t_ai.insert(r+1,-(__int64)c); t_iai.insert(r+1,-(__int64)c*(__int64)(r+1)); } } // t_ai.print(); // t_iai.print(); } return 0; }