题目链接
http://poj.org/problem?id=3468
题意
给定由N个整数组成的序列。
对这个序列进行如下两种操作:
(1)Q a b:输出序列第a个元素到第b个元素的和,下标从1开始,包括第a个和第b个
(2)C a b c:对序列第a个元素到第b个元素的所有元素都累加c
题解
线段树区间更新,区间查询问题。
线段树原理:每一个节点代表一个区间上的信息,对区间进行修改,即对若干个对应的节点进行修改。而考虑到暴力修改所有节点的时间复杂度难以接受,所以引入一个延迟标记。也就是说只修改一个节点,而对该节点的子节点暂时不修改,等到下一次需要用到该节点的子节点时(包括更新和修改),再根据延迟标记进行修改。
modify(p,l,r,x,y,z):将区间[x,y]的值都加z
query(p,l,r,x,y):返回区间[x,y]的和
up(p):由下向上更新节点p的值
down(p,l,r):下传延迟标记并修改子节点
代码
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=4e5+100;
ll s[maxn],delta[maxn];
void down(int p,int l,int r)
{
int mid=(l+r)/2;
s[p*2]+=(mid-l+1)*delta[p];
s[p*2+1]+=(r-mid)*delta[p];
delta[p*2]+=delta[p];
delta[p*2+1]+=delta[p];
delta[p]=0;
}
void up(int p)
{
s[p]=s[p*2]+s[p*2+1];
}
ll query(int p,int l,int r,int x,int y)
{
if(x<=l && r<=y)
{
return s[p];
}
down(p,l,r);
int mid=(l+r)/2;
ll ret=0;
if(x<=mid) ret+=query(p*2,l,mid,x,y);
if(y>mid) ret+=query(p*2+1,mid+1,r,x,y);
up(p);
return ret;
}
void modify(int p,int l,int r,int x,int y,int z)
{
if(x<=l && r<=y)
{
s[p]+=(r-l+1)*z;
delta[p]+=z;
return;
}
down(p,l,r);
int mid=(l+r)/2;
if(x<=mid) modify(p*2,l,mid,x,y,z);
if(y>mid) modify(p*2+1,mid+1,r,x,y,z);
up(p);
}
int main()
{
int N,Q;
while(~scanf("%d%d",&N,&Q))
{
memset(s,0,sizeof(s));
memset(delta,0,sizeof(delta));
for(int i=1;i<=N;i++)
{
int a;
scanf("%d",&a);
modify(1,1,N,i,i,a);
}
while(Q--)
{
char ch;
getchar();
scanf("%c",&ch);
int x,y,z;
if(ch=='Q')
{
scanf("%d%d",&x,&y);
printf("%lld\n",query(1,1,N,x,y));
}
else
{
scanf("%d%d%d",&x,&y,&z);
modify(1,1,N,x,y,z);
}
}
}
return 0;
}