POJ 3468(成段更新)
来源:互联网 发布:unity3d手机游戏逆向 编辑:程序博客网 时间:2024/06/06 05:47
Language:
线段树的成段更新
Time Limit: 5000MS Memory Limit: 131072KTotal Submissions: 38010 Accepted: 11017Case Time Limit: 2000MS
Description
对于数列 A1, A2, ... , AN. 你要进行2个操作:将一个区间的数同加上某个数,输出一段区间的和。
Input
第一行2个整数表示数列长度和操作次数. 1 ≤ N,Q ≤ 100000.
第二行为数列 A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
接下来的Q行操作:
"C a b c" 表示将 Aa, Aa+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;}
- POJ 3468(成段更新)
- poj 3468 线段树 成段更新
- POJ 3468 成段更新 模板题
- poj 3468 线段树--成段更新
- poj 3468线段树 成段更新
- POJ 2991 Crane(成段更新)
- POJ 3468 【线段树区间更新-成段更新】
- POJ 3468 A Simple Problem with Integers 成段更新
- poj 3468A Simple Problem with Integers(成段更新)
- POJ 3667 Hotel(成段更新,区间合并)
- POJ 2528 Mayor's posters(成段更新)
- poj 1436 成段更新(区间覆盖)
- POJ 3468 A Simple Problem with Integers(成段更新)
- poj 3468 线段树 区间内的线段和(成段更新)
- poj 3468 A Simple Problem with Integers(线段树|成段更新,区间查询)
- poj 3468 A Simple Problem with Integers(线段树,成段更新)
- POJ 3468 A Simple Problem with Integers 线段树 (成段更新)
- [ACM] poj 3468 A Simple Problem with Integers(线段树,成段更新,懒惰标记)
- Spring JdbcTemplate使用
- android4.0 编译时,不要产生odex文件
- 关于开源 和 保护程序员利益的讨论
- VS 生成事件 文件拷贝
- Android自定义属性,attr format取值类型
- POJ 3468(成段更新)
- main 和launcher
- 仿 Google Reader 随滚动条滚动加载页面效果
- gsoap c与java web之间传输字符串中文乱码问题
- 小结给内核打补丁
- DWR+extjs开发小结
- Linux 查看CPU信息、机器型号等硬件信息
- 快速排序算法的C/C++语言实现
- linux XFRM整体框架简单分析