POJ3468 A Simple Problem with Integers

来源:互联网 发布:海文工程项目网络计划 编辑:程序博客网 时间:2024/06/03 21:06

题目链接

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