码迷,mamicode.com
首页 > 其他好文 > 详细

P3372 【模板】线段树 1

时间:2019-08-02 22:34:27      阅读:103      评论:0      收藏:0      [点我收藏+]

标签:树结构   name   names   print   pre   root   题目   规模   结果   

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入格式:

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

输出格式:

输出包含若干行整数,即为所有操作2的结果。

输入样例:

5 5

1 5 4 2 3

2 2 4

1 2 3 2

2 3 4

1 1 5 1

2 1 4

输出样例:

11

8

20

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

(数据已经过加强^_^,保证在int64/long long数据范围内)

样例说明:

技术图片

代码:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int N=1000010;;

long long n,m,a[N];
long long ans[N<<2],tag[N<<2];

void pushup(long long p) {
    ans[p]=ans[p<<1]+ans[p<<1|1];
}

void build(long long p,long long l,long long r) {
    tag[p]=0;
    if(l==r) {
        ans[p]=a[l];
        return ;
    }
    long long mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
}

void f(long long p,long long l,long long r,long long k) {
    tag[p]=tag[p]+k;
    ans[p]=ans[p]+k*(r-l+1);
}

void pushdown(long long p,long long l,long long r) {
    long long mid=(l+r)>>1;
    f(p<<1,l,mid,tag[p]);
    f(p<<1|1,mid+1,r,tag[p]);
    tag[p]=0;
}

void update(long long rt,long long nr,long long l,long long r,long long p,long long k) {
    if(rt<=l&&r<=nr) {
        ans[p]+=k*(r-l+1);
        tag[p]+=k;
        return ;
    }
    pushdown(p,l,r);
    long long mid=(l+r)>>1;
    if(rt<=mid)
        update(rt,nr,l,mid,p<<1,k);
    if(nr>mid)
        update(rt,nr,mid+1,r,p<<1|1,k);
    pushup(p);
}

long long query(long long x,long long y,long long l,long long r,long long p) {
    long long res=0;
    if(x<=l&&r<=y)
        return ans[p];
    long long mid=(l+r)>>1;
    pushdown(p,l,r);
    if(x<=mid)
        res+=query(x,y,l,mid,p<<1);
    if(y>mid)
        res+=query(x,y,mid+1,r,p<<1|1);
    return res;
}

int main() {
    long long a1,b,c,d,e,f;
    scanf("%lld%lld",&n,&m);
    for(long long i=1; i<=n; i++)
        scanf("%lld",&a[i]);
    build(1,1,n);
    while(m--) {
        scanf("%lld",&a1);
        if(a1==1) {
            scanf("%lld%lld%lld",&b,&c,&d);
            update(b,c,1,n,1,d);
        }
        if(a1==2) {
            scanf("%lld%lld",&e,&f);
            printf("%lld\n",query(e,f,1,n,1));
        }
    }
    return 0;
}

 

线段树2:

 
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
//题目中给的p
int p;
//暂存数列的数组
long long a[100007];
//线段树结构体,v表示此时的答案,mul表示乘法意义上的lazytag,add是加法意义上的
struct node{
    long long v, mul, add;
}st[400007];
//buildtree
void bt(int root, int l, int r){
//初始化lazytag
    st[root].mul=1;
    st[root].add=0;
    if(l==r){
        st[root].v=a[l];
    }
    else{
        int m=(l+r)/2;
        bt(root*2, l, m);
        bt(root*2+1, m+1, r);
        st[root].v=st[root*2].v+st[root*2+1].v;
    }
    st[root].v%=p;
    return ;
}
//核心代码,维护lazytag
void pushdown(int root, int l, int r){
    int m=(l+r)/2;
//根据我们规定的优先度,儿子的值=此刻儿子的值*爸爸的乘法lazytag+儿子的区间长度*爸爸的加法lazytag
    st[root*2].v=(st[root*2].v*st[root].mul+st[root].add*(m-l+1))%p;
    st[root*2+1].v=(st[root*2+1].v*st[root].mul+st[root].add*(r-m))%p;
//很好维护的lazytag
    st[root*2].mul=(st[root*2].mul*st[root].mul)%p;
    st[root*2+1].mul=(st[root*2+1].mul*st[root].mul)%p;
    st[root*2].add=(st[root*2].add*st[root].mul+st[root].add)%p;
    st[root*2+1].add=(st[root*2+1].add*st[root].mul+st[root].add)%p;
//把父节点的值初始化
    st[root].mul=1;
    st[root].add=0;
    return ;
}
//update1,乘法,stdl此刻区间的左边,stdr此刻区间的右边,l给出的左边,r给出的右边
void ud1(int root, int stdl, int stdr, int l, int r, long long k){
//假如本区间和给出的区间没有交集
    if(r<stdl || stdr<l){
        return ;
    }
//假如给出的区间包含本区间
    if(l<=stdl && stdr<=r){
        st[root].v=(st[root].v*k)%p;
        st[root].mul=(st[root].mul*k)%p;
        st[root].add=(st[root].add*k)%p;
        return ;
    }
//假如给出的区间和本区间有交集,但是也有不交叉的部分
//先传递lazytag
    pushdown(root, stdl, stdr);
    int m=(stdl+stdr)/2;
    ud1(root*2, stdl, m, l, r, k);
    ud1(root*2+1, m+1, stdr, l, r, k);
    st[root].v=(st[root*2].v+st[root*2+1].v)%p;
    return ;
}
//update2,加法,和乘法同理
void ud2(int root, int stdl, int stdr, int l, int r, long long k){
    if(r<stdl || stdr<l){
        return ;
    }
    if(l<=stdl && stdr<=r){
        st[root].add=(st[root].add+k)%p;
        st[root].v=(st[root].v+k*(stdr-stdl+1))%p;
        return ;
    }
    pushdown(root, stdl, stdr);
    int m=(stdl+stdr)/2;
    ud2(root*2, stdl, m, l, r, k);
    ud2(root*2+1, m+1, stdr, l, r, k);
    st[root].v=(st[root*2].v+st[root*2+1].v)%p;
    return ;
}
//访问,和update一样
long long query(int root, int stdl, int stdr, int l, int r){
    if(r<stdl || stdr<l){
        return 0;
    }
    if(l<=stdl && stdr<=r){
        return st[root].v;
    }
    pushdown(root, stdl, stdr);
    int m=(stdl+stdr)/2;
    return (query(root*2, stdl, m, l, r)+query(root*2+1, m+1, stdr, l, r))%p;
}
int main(){
    int n, m;
    scanf("%d%d%d", &n, &m, &p);
    for(int i=1; i<=n; i++){
        scanf("%lld", &a[i]);
    }
    bt(1, 1, n);
    while(m--){
        int chk;
        scanf("%d", &chk);
        int x, y;
        long long k;
        if(chk==1){
            scanf("%d%d%lld", &x, &y, &k);
            ud1(1, 1, n, x, y, k);
        }
        else if(chk==2){
            scanf("%d%d%lld", &x, &y, &k);
            ud2(1, 1, n, x, y, k);
        }
        else{
            scanf("%d%d", &x, &y);
            printf("%lld\n", query(1, 1, n, x, y));
        }
    }
    return 0;
}

 

P3372 【模板】线段树 1

标签:树结构   name   names   print   pre   root   题目   规模   结果   

原文地址:https://www.cnblogs.com/mysh/p/11291424.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!