<线段树系列3> codevs 1082 线段树练习3

来源:互联网 发布:和硕鼠类似的软件 编辑:程序博客网 时间:2024/05/16 23:59

codevs 1082 线段树练习3

—-这是我学习线段树的模板题
题目描述 Description

给你N个数,有两种操作:
1:给区间[a,b]的所有数增加X
2:询问区间[a,b]的数的和。

输入描述 Input Description

第一行一个正整数n,接下来n行n个整数,
再接下来一个正整数Q,每行表示操作的个数,
如果第一个数是1,后接3个正整数,
表示在区间[a,b]内每个数增加X,如果是2,
表示操作2询问区间[a,b]的和是多少。
pascal选手请不要使用readln读入

输出描述 Output Description

对于每个询问输出一行一个答案

样例输入 Sample Input

3
1
2
3
2
1 2 3 2
2 2 3

样例输出 Sample Output

9

数据范围及提示 Data Size & Hint

1<=n<=200000
1<=q<=200000

一句话:区间修改,区间查询

代码:

#include<iostream>#include<cstdio>#define ll long longusing namespace std;const int maxn=2e5+5;struct hh{    int l,r;    ll summ,add;}tree[maxn<<2];int a[maxn];void Updata(int n,int x){    tree[n].summ+=x*(tree[n].r-tree[n].l+1);}void Spread(int n){    ll &ax=tree[n].add;    if(ax)    {        tree[n<<1].add+=ax;        tree[n<<1|1].add+=ax;        Updata(n<<1,ax);        Updata(n<<1|1,ax);        ax=0;    }}ll Ask(int l,int r,int n){    int lx=tree[n].l;    int rx=tree[n].r;    if(l<=lx&&r>=rx)      return tree[n].summ;    int mid=(lx+rx)>>1;    Spread(n);//查询时不要忘记下放标记     ll ans=0;    if(l<=mid) ans+=Ask(l,r,n<<1);    if(r>mid) ans+=Ask(l,r,n<<1|1);    return ans;}void Add(int l,int r,int n,ll x){    if(tree[n].l>=l&&tree[n].r<=r)    {        tree[n].add+=x;        Updata(n,x);        return;    }    Spread(n);    int mid=(tree[n].l+tree[n].r)>>1;    if(l<=mid) Add(l,r,n<<1,x);    if(r>mid) Add(l,r,n<<1|1,x);    tree[n].summ=tree[n<<1].summ+tree[n<<1|1].summ;}ll Build(int l,int r,int n){    tree[n].l=l;    tree[n].r=r;    if(l==r)     {        tree[n].summ=a[l];        return a[l];    }    int mid=(l+r)>>1;    return tree[n].summ=Build(l,mid,n<<1)+Build(mid+1,r,n<<1|1);}int main(){    int n,m;    scanf("%d",&n);    for(int i=1;i<=n;++i) scanf("%d",a+i);    Build(1,n,1);    scanf("%d",&m);    while(m--)    {        int x,y,z,w;        scanf("%d%d%d",&x,&y,&z);        if(x==1)        {            scanf("%d",&w);            Add(y,z,1,w);        }        else cout<<Ask(y,z,1)<<"\n";    }    return 0;}