POJ 3468(成段更新)

来源:互联网 发布:unity3d手机游戏逆向 编辑:程序博客网 时间:2024/06/06 05:47

Language:
线段树的成段更新
Time Limit: 5000MS Memory Limit: 131072KTotal Submissions: 38010 Accepted: 11017Case Time Limit: 2000MS

Description

对于数列 A1A2, ... , AN. 你要进行2个操作:将一个区间的数同加上某个数,输出一段区间的和。

Input

第一行2个整数表示数列长度和操作次数. 1 ≤ N,Q ≤ 100000.
第二行为数列 A1A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
接下来的Q行操作:
"C a b c" 表示将 AaAa+1, ... , Ab.加上c. -10000 ≤ c ≤ 10000.
"Q a b" 输出区间[a,b]的和。

Output

输出所有询问的答案,每行1个。

Sample Input

10 51 2 3 4 5 6 7 8 9 10Q 4 4Q 1 10Q 2 4C 3 6 3Q 2 4

Sample Output

455915

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;}











原创粉丝点击