zoukankan      html  css  js  c++  java
  • 线段树模板

    定义
    线段树是一种特殊的二叉树,每个节点代表原数组的一段区间,根节点代表整个区间,叶节点代表长度为1的区间。
    一般应用:
    利用线段树快速求区间的最值,求和等。
    使用延迟标记可以很好地支持区间修改。
    单次修改和查询的时间复杂度都是O(log n)。
    一:

    题目描述

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

    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的结果。

    输入输出样例
    输入样例#1: 复制

    5 5
    1 5 4 2 3
    2 2 4
    1 2 3 2
    2 3 4
    1 1 5 1
    2 1 4

    输出样例#1: 复制

    11
    8
    20

    说明

    时空限制:1000ms,128M

    数据规模:

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

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

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

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

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn=100005;
    ll n,m,a[maxn];
    int cnt,rt;
    struct Node{      
        ll sum;  //sum代表此区间的值。
        int lazy;  //lazy表示延迟标记,修改区间值时需要用到
        int l,r;   //左右区间
        int ls,rs;  //左右儿子
    }node[maxn*2];
    inline void pushup(ll x){     //求值更新区间
        ll lson=node[x].ls,rson=node[x].rs;
        node[x].sum=node[lson].sum+node[rson].sum;  //求值
        node[x].l=node[lson].l;   //更新区间
        node[x].r=node[rson].r;
    }
    inline void pushdown(ll x){      //标记下放
        ll lson=node[x].ls,rson=node[x].rs;
        node[lson].sum+=node[x].lazy*(node[lson].r-node[lson].l+1);
        node[rson].sum+=node[x].lazy*(node[rson].r-node[rson].l+1);
        node[lson].lazy+=node[x].lazy;
        node[rson].lazy+=node[x].lazy;
        node[x].lazy=0;    //标记还原
    }
    inline void build(ll L,ll R,ll cur){     //建树
        if(L==R){    //如果是叶节点
            node[cur].ls=node[cur].rs=-1;    //没有儿子
            node[cur].l=node[cur].r=L;
            node[cur].sum=a[L];
            node[cur].lazy=0;
            return;
        }
        ll mid=(L+R)/2;
        node[cur].ls=++cnt,node[cur].rs=++cnt;
        build(L,mid,node[cur].ls);         //递归建树
        build(mid+1,R,node[cur].rs);
        pushup(cur);          //更新区间lazy
    }
    inline ll query(ll L,ll R,ll cur){         //求和函数
        if(L<=node[cur].l && node[cur].r<=R)
            return node[cur].sum;     //在所求区间内
        pushdown(cur);       //标记下放
        ll tot=0;
        ll mid=(node[cur].l+node[cur].r)/2;
        if(L<=mid)  tot+=query(L,R,node[cur].ls);   //区间落在左子树
        if(mid<R)   tot+=query(L,R,node[cur].rs);   //区间落在右子树
        return tot;
    }
    inline void update(ll L,ll R,ll c,ll cur){   //修改区间   
        if(L<=node[cur].l && node[cur].r<=R){   //落在区间内
            node[cur].sum+=c*(node[cur].r-node[cur].l+1);
            node[cur].lazy+=c;
            return; 
        }
        pushdown(cur);  //标记下放
        ll mid=(node[cur].l+node[cur].r)/2;
        if(L<=mid)  update(L,R,c,node[cur].ls);
        if(mid<R)   update(L,R,c,node[cur].rs);
        pushup(cur);   //修改子树
    }
    int main(){
        scanf("%lld%lld",&n,&m);
        for(register int i=1;i<=n;i++)
            scanf("%lld",&a[i]);    
        rt=++cnt;    //根节点编号为1
        build(1,n,rt);     //建树
        for(register int i=1;i<=m;i++){
            ll x;
            scanf("%lld",&x);
            if(x==1){
                ll aa,bb,cc;
                scanf("%lld%lld%lld",&aa,&bb,&cc);
                update(aa,bb,cc,rt);
            } 
            else{
                ll aa,bb;
                scanf("%lld%lld",&aa,&bb);
                printf("%lld
    ",query(aa,bb,rt));
            }
        }
        return 0;
    }

    二:

    题目描述

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

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

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

    3.求出某区间每一个数的和
    输入输出格式
    输入格式:

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

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

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

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

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

    操作3: 格式:3 x y 含义:输出区间[x,y]内每个数的和对P取模所得的结果

    输出格式:

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

    输入输出样例
    输入样例#1: 复制

    5 5 38
    1 5 4 2 3
    2 1 4 1
    3 2 5
    1 2 4 2
    2 3 5 5
    3 1 4

    输出样例#1: 复制

    17
    2

    说明

    时空限制:1000ms,128M

    数据规模:

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

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

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

    (数据已经过加强^_^)

        此题要定义一个加法lazy标记和乘法lazy标记,更新时如果是乘法
    ,就将加法标记同乘。其余和第一个模板类似。
    
    #include<bits/stdc++.h>
    #define maxn 100005
    #define ll long long
    using namespace std;
    ll n,m,cnt,rt,p;
    ll a[maxn];
    struct Node{
        ll sum;
        ll add_lazy,mul_lazy;
        ll l,r;
        ll ls,rs;
    };
    Node node[2*maxn];
    inline void pushup(ll x){
         ll lson=node[x].ls;
         ll rson=node[x].rs;
         node[x].sum=node[lson].sum+node[rson].sum;
         node[x].sum%=p;
         node[x].l=node[lson].l;
         node[x].r=node[rson].r;
    }
    inline void pushdown(ll x){
        ll lson=node[x].ls;
        ll rson=node[x].rs;
        node[lson].sum*=node[x].mul_lazy;
        node[rson].sum*=node[x].mul_lazy;
        node[lson].sum%=p;
        node[rson].sum%=p;
        node[lson].mul_lazy*=node[x].mul_lazy;
        node[rson].mul_lazy*=node[x].mul_lazy;
        node[lson].add_lazy*=node[x].mul_lazy;
        node[rson].add_lazy*=node[x].mul_lazy;
        node[lson].add_lazy%=p;
        node[rson].add_lazy%=p;
        node[lson].mul_lazy%=p;
        node[rson].mul_lazy%=p;
        node[x].mul_lazy=1;
        node[lson].sum+=node[x].add_lazy*(node[lson].r-node[lson].l+1);
            node[rson].sum+=node[x].add_lazy*(node[rson].r-node[rson].l+1);
            node[lson].sum%=p;
            node[rson].sum%=p;
            node[lson].add_lazy+=node[x].add_lazy;
            node[rson].add_lazy+=node[x].add_lazy;
            node[lson].add_lazy%=p;
            node[rson].add_lazy%=p; 
            node[x].add_lazy=0; 
    }
    inline void build(ll L,ll R,ll cur){
        if(L==R){
            node[cur].ls=node[cur].rs=-1;
            node[cur].l=node[cur].r=L;
            node[cur].sum=a[L];
            return;
        }
        node[cur].add_lazy=0;
        node[cur].mul_lazy=1;
        ll mid=(L+R)/2;
        node[cur].ls=++cnt;
        node[cur].rs=++cnt;
        build(L,mid,node[cur].ls);
        build(mid+1,R,node[cur].rs);
        pushup(cur);
    }
    inline void add_update(ll L,ll R,ll c,ll cur){
        if(L<=node[cur].l && node[cur].r<=R){
            node[cur].sum+=c*(node[cur].r-node[cur].l+1);
            node[cur].sum%=p;
            node[cur].add_lazy+=c;
            node[cur].add_lazy%=p;
            return;
        }
        pushdown(cur);
        ll mid=(node[cur].l+node[cur].r)/2;
        if(L<=mid)  add_update(L,R,c,node[cur].ls);
        if(mid<R)   add_update(L,R,c,node[cur].rs);
        pushup(cur);
    }
    inline void mul_update(ll L,ll R,ll c,ll cur){
        if(L<=node[cur].l && node[cur].r<=R){
            node[cur].sum*=c;
            node[cur].sum%=p;
            node[cur].add_lazy*=c;
            node[cur].mul_lazy*=c;
            node[cur].add_lazy%=p;
            node[cur].mul_lazy%=p;return; 
        }
        pushdown(cur);
        ll mid=(node[cur].l+node[cur].r)/2;
        if(L<=mid)  mul_update(L,R,c,node[cur].ls);
        if(mid<R)   mul_update(L,R,c,node[cur].rs);
        pushup(cur);
    }
    inline ll query(ll L,ll R,ll cur){
        if(L<=node[cur].l && node[cur].r<=R)
            return node[cur].sum;
        pushdown(cur);
        ll mid=(node[cur].l+node[cur].r)/2;
        ll tot=0;
        if(L<=mid)  tot+=query(L,R,node[cur].ls)%p;
        if(mid<R)   tot+=query(L,R,node[cur].rs)%p;
        return tot%p;    
    }
    int main(){
        scanf("%lld%lld%lld",&n,&m,&p);
        for(register int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        rt=++cnt;
        build(1,n,rt);
        for(register int i=1;i<=m;i++){
            int a;
            scanf("%d",&a);
            if(a==1){
                ll x,y,z;
                scanf("%lld%lld%lld",&x,&y,&z);
                mul_update(x,y,z,rt);
            }
            else if(a==2){
                ll x,y,z;
                scanf("%lld%lld%lld",&x,&y,&z);
                add_update(x,y,z,rt);
            }
            else{
                ll x,y;
                scanf("%lld%lld",&x,&y);
                printf("%lld
    ",query(x,y,rt)%p);
            }
        }
        return 0;
    }
  • 相关阅读:
    Linux——强制退出命令
    2019-08-07-安装jdk1.8配置环境变量
    2019-08-07-查看当前的系统版本-中文乱码显示
    2019-08-07-centos系统上文件出现乱码
    2019-08-06-centos6.4关闭防火墙-设置通过防火墙的端口
    2019-08-06-写批处理
    2019-08-06-centos 打包
    2019-07-26_解密黑客攻击前的准备
    2019-07-24-DOS 常用命令大全
    2019-07-24-windows一些常用命令-netsh
  • 原文地址:https://www.cnblogs.com/sdfzsyq/p/9677172.html
Copyright © 2011-2022 走看看