概述
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;
}
最后
以上就是愉快鲜花为你收集整理的POJ 3468(成段更新)的全部内容,希望文章能够帮你解决POJ 3468(成段更新)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复