我是靠谱客的博主 愉快鲜花,最近开发中收集的这篇文章主要介绍POJ 3468(成段更新),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Language:
线段树的成段更新
Time Limit: 5000MS Memory Limit: 131072K
Total Submissions: 38010 Accepted: 11017
Case 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 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(成段更新)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部