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

    线段树板子

    线段树最原始的功能是区间求和

    因为是树状结构,使得查询和修改的复杂度都为O(log n)级别

    又因为有懒标记的存在,大大降低了复杂度

    所以我觉得,树状结构式线段树的基本

    懒标记是线段树的灵魂

    #include<iostream>
    using namespace std;
    #define ll long long
    #define lid (id << 1)
    #define rid (id << 1) | 1//位运算可以降低常数
    const ll maxn = 100110;
    ll a[maxn];
    struct sag_tree{ll l,r;ll lazy;ll sum;}tree[maxn << 2];
    
    void build(ll id,ll l,ll r){
        tree[id].l = l;
        tree[id].r = r;
        if(l == r){
            tree[id].sum = a[l];
            return;//记得return
            }
        ll mid = (l + r) >> 1;
        build(lid,l,mid);
        build(rid,mid + 1,r);
        tree[id].sum = tree[lid].sum + tree[rid].sum;
        }
    
    void pushdown(ll id){//懒标记下放(pushdown(i)是更新儿子而不是自己)
        if(tree[id].lazy != 0 && tree[id].l != tree[id].r){
            ll val = tree[id].lazy;
            tree[lid].lazy += val;
            tree[rid].lazy += val;
            tree[lid].sum += val * (tree[lid].r - tree[lid].l + 1);
            tree[rid].sum += val * (tree[rid].r - tree[rid].l + 1);
            tree[id].lazy = 0;//记得归零
            }
        }
    
    void update(ll id,ll val,ll l,ll r){
        pushdown(id);
        if(tree[id].l == l && tree[id].r == r){
            tree[id].lazy += val;
            tree[id].sum += val * (r - l + 1);
            return;
            }
        ll mid = (tree[id].l + tree[id].r) >> 1;
        if(mid < l){
            update(rid,val,l,r);
            }
        else if(mid >= r){
            update(lid,val,l,r);
            }
        else{
            update(lid,val,l,mid);
            update(rid,val,mid + 1,r);
            }
        tree[id].sum = tree[lid].sum + tree[rid].sum;
        }
        
    ll getsum(ll id,ll l,ll r){
        pushdown(id);
        if(tree[id].l == l && tree[id].r == r){
            return tree[id].sum;
            }
        ll mid = (tree[id].l + tree[id].r) >> 1;
        if(mid < l){
            return getsum(rid,l,r);
            }
        else if(mid >= r){
            return getsum(lid,l,r);
            }
        else{
            return getsum(lid,l,mid) + getsum(rid,mid + 1,r);
            }
        }
        
    int main(){
        ll num,nask;
        cin>>num>>nask;
        for(ll i = 1;i <= num;i++)cin>>a[i];
        build(1,1,num);
        ll ask,x,y,v;
        for(ll i = 1;i <= nask;i++){
            cin>>ask;
            if(ask == 1){
                cin>>x>>y>>v;
                update(1,v,x,y);
                }
            if(ask == 2){
                cin>>x>>y;
                cout<<getsum(1,x,y)<<endl;
                }
            }
        return 0;
        }
    

    还有就是既涉及乘法又涉及加法是要有一个优先级的概念

    #include<iostream>
    #include<cstdio>
    using namespace std;
    long long RD(){
        int out = 0,flag = 1;char c = getchar();
        while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
        while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
        return flag * out;
        }
    #define lid (id << 1)
    #define rid (id << 1) | 1
    #define ll long long
    const long long maxn = 100100;
    long long num,nask,P;
    long long a[maxn];
    struct sag_tree{long long l,r;long long sum,add,mul;}tree[maxn << 2];
    void build(long long id,long long l,long long r){
        tree[id].add = 0;
        tree[id].mul = 1;
        tree[id].l = l;
        tree[id].r = r;
        if(l == r){
            tree[id].sum = a[l];
            return ;
            }
        long long mid = (l + r) >> 1;
        build(lid,l,mid);
        build(rid,mid + 1,r);
        tree[id].sum = (tree[lid].sum + tree[rid].sum) % P;
        }
    
    void pushdown(long long id){
        if((tree[id].add != 0 || tree[id].mul != 1) && tree[id].l != tree[id].r){
            long long add = tree[id].add;
            long long mul = tree[id].mul;
            tree[lid].sum *= mul;//乘法
            tree[lid].sum %= P;
            tree[lid].sum += (tree[lid].r - tree[lid].l + 1) * add;//加法
            tree[lid].sum %= P;
            
            tree[rid].sum *= mul;
            tree[rid].sum %= P;
            tree[rid].sum += (tree[rid].r - tree[rid].l + 1) * add;
            tree[rid].sum %= P;//更新左右儿子
            
            tree[lid].mul *= mul;
            tree[lid].mul %= P;
            tree[lid].add = (tree[lid].add * mul + tree[id].add) % P;
            
            tree[rid].mul *= mul;
            tree[rid].mul %= P;
            tree[rid].add = (tree[rid].add * mul + tree[id].add) % P;
            
            tree[id].add = 0;
            tree[id].mul = 1;
            }
        }
        
    void update1(long long id,long long val,long long l,long long r){
        pushdown(id);
        if(tree[id].l == l && tree[id].r == r){
            tree[id].sum *= val;
            tree[id].sum %= P;
            tree[id].mul *= val;
            tree[id].mul %= P;
            tree[id].add *= val;
            tree[id].add %= P;
            return ;
            }
        long long mid = (tree[id].l + tree[id].r) >> 1;
        if(mid < l){
            update1(rid,val,l,r);
            }
        else if(mid >= r){
            update1(lid,val,l,r);
            }
        else{
            update1(lid,val,l,mid);
            update1(rid,val,mid + 1,r);
            }
        tree[id].sum = (tree[lid].sum + tree[rid].sum) % P;
        }
        
    void update2(long long id,long long val,long long l,long long r){
        
        pushdown(id);
        if(tree[id].l == l && tree[id].r == r){
            tree[id].sum += (tree[id].r - tree[id].l + 1) * val;
            tree[id].sum %= P;
            tree[id].add += val;
            tree[id].add %= P;
            return ;
            }
        long long mid = (tree[id].l + tree[id].r) >> 1;
        if(mid < l){
            update2(rid,val,l,r);
            }
        else if(mid >= r){
            update2(lid,val,l,r);
            }
        else{
            update2(lid,val,l,mid);
            update2(rid,val,mid + 1,r);
            }
        tree[id].sum = (tree[lid].sum + tree[rid].sum) % P;
        }
    long long query(long long id,long long l,long long r){
        pushdown(id);
        if(tree[id].l == l && tree[id].r == r){
            return tree[id].sum;
            }
        long long mid = (tree[id].l + tree[id].r) >> 1;
        if(mid < l){
            return query(rid,l,r);
            }
        else if(mid >= r){
            return query(lid,l,r);
            }
        else{
            return (query(lid,l,mid) + query(rid,mid + 1,r)) % P;
            }
        }
    int main(){
        num = RD();nask = RD();P = RD();
        for(long long i = 1;i <= num;i++)a[i] = RD();
        build(1,1,num);
        long long ask,l,r,val;
        for(long long i = 1;i <= nask;i++){
            ask = RD();l = RD();r = RD();
            if(ask == 1){
                val = RD();
                update1(1,val,l,r);
                }
            else if(ask == 2){
                val = RD();
                update2(1,val,l,r);
                }
            else{
                printf("%d
    ",query(1,l,r));
                }
            }
        return 0;
        }
    

    线段树的其他用法

    我们可以人为的定义线段树的概念【在建树里得到体现】,对修改及查询函数做适当的修改,从而解决很多问题

    很基础的例子就是查询一个区间内的最值:

    P1531 I Hate It

    题目背景

    很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。这让很多学生很反感。

    题目描述

    不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩

    输入输出格式

    输入格式:
    第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。学生ID编号分别从1编到N。第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。当C为'U'的时候,表示这是一条更新操作,如果当前A学生的成绩低于B,则把ID为A的学生的成绩更改为B,否则不改动。

    输出格式:
    对于每一次询问操作,在一行里面输出最高成绩


    典型的最大值查询问题。因为涉及分数修改,所以我们用线段树解决问题

    我们定义一个线段树的max表示其管理区间内的最大值,建树时不更新为和,而是更新为最大值(修改定义)

    对于更新函数,我们在修改后回溯时同样返回区间最大值

    对于询问函数,若刚好为这个区间,则直接返回这个区间的max,若横跨区间,则找出这几个区间内的max,返回

    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define lid (id << 1)
    #define rid (id << 1) | 1
    const int maxn = 200100;
    int num,na;
    
    int a[maxn];
    struct sag_tree{int l,r,sum;}tree[maxn << 2];
    
    void build(int id,int l,int r){
        tree[id].l = l;
        tree[id].r = r;
        if(l == r){
            tree[id].sum = a[l];
            return;
            }
        int mid = (l + r) >> 1;
        build(lid,l,mid);
        build(rid,mid + 1,r);
        tree[id].sum = max(tree[lid].sum,tree[rid].sum);//修改定义
        }
    void update(int id,int p,int l,int r){
        if(tree[id].l == l && tree[id].r == r){
            tree[id].sum = max(tree[id].sum,p);
            return;
            }
        int mid = (tree[id].l + tree[id].r) >> 1;
        if(mid < l){
            update(rid,p,l,r);
            }
        else if(mid >= r){
            update(lid,p,l,r);
            }
        tree[id].sum = max(tree[lid].sum,tree[rid].sum);//回溯维护区间最大值
        }
    int query(int id,int l,int r){
        if(tree[id].l == l && tree[id].r == r){
            return tree[id].sum;
            }
        int mid = (tree[id].l + tree[id].r) >> 1;
        if(mid < l){
            return query(rid,l,r);
            }
        else if(mid >= r){
            return query(lid,l,r);
            }
        else{
            return max(query(lid,l,mid),query(rid,mid + 1,r));
            }
        }
    int main(){
        cin>>num>>na;
        for(int i = 1;i <= num;i++)cin>>a[i];
        build(1,1,num);
        char ask;
        int a,b;
        for(int i = 1;i <= na;i++){
            cin>>ask>>a>>b;
            if(ask == 'Q'){
                cout<<query(1,a,b)<<endl;
                }
            else{
                update(1,b,a,a);
                }
            }
        return 0;
        }
    

    P2826 [USACO08NOV]光开关Light Switching

    题目描述

    灯是由高科技——外星人鼠标操控的。你只要左击两个灯所连的鼠标,

    这两个灯,以及之间的灯都会由暗变亮,或由亮变暗。右击两个灯所连的鼠

    标,你就可以知道这两个灯,以及之间的灯有多少灯是亮的。起初所有灯都是暗的,你的任务是在LZ之前算出灯的亮灭。

    输入输出格式

    输入格式:
    第1 行: 用空格隔开的两个整数N 和M,n 是灯数

    第2..M+1 行: 每行表示一个操作, 有三个用空格分开的整数: 指令号, S_i 和E_i

    第1 种指令(用0 表示)包含两个数字S_i 和E_i (1 <= S_i <= E_i <= N), 它们表示起

    始开关和终止开关. 表示左击

    第2 种指令(用1 表示)同样包含两个数字S_i 和E_i (1 <= S_i <= E_i <= N), 不过这

    种指令是询问从S_i 到E_i 之间的灯有多少是亮着的.


    同样的

    我们定义一个on,on == 1为亮;on == 0为暗

    区间修改:区间内 1 -> 0 或 0 -> 1

    查询:直接求和即可

    懒标记下放:(因为题意,这题是可以标记下放的,所以)我们规定lazy == 1 表示儿子需要转换状态,lazy == 0 表示不需要,若更新时有lazy == 1,直接改为0即可

    #include<iostream>
    using namespace std;
    #define ll long long
    #define lid (id << 1)
    #define rid (id << 1) | 1
    const ll maxn = 100110;
    ll a[maxn];
    struct sag_tree{ll l,r;ll lazy;ll sum;}tree[maxn << 2];
    
    void build(ll id,ll l,ll r){
        tree[id].l = l;
        tree[id].r = r;
        if(l == r){
            tree[id].sum = a[l];
            return;
            }
        ll mid = (l + r) >> 1;
        build(lid,l,mid);
        build(rid,mid + 1,r);
        tree[id].sum = tree[lid].sum + tree[rid].sum;
        }
    
    void pushdown(ll id){
        if(tree[id].lazy == 1 && tree[id].l != tree[id].r){
            tree[lid].lazy = 1 - tree[lid].lazy;
            tree[rid].lazy = 1 - tree[rid].lazy;
            tree[lid].sum = (tree[lid].r - tree[lid].l + 1) - tree[lid].sum;
            tree[rid].sum = (tree[rid].r - tree[rid].l + 1) - tree[rid].sum;
            tree[id].lazy = 0;
            }
        }
    
    void update(ll id,ll l,ll r){
        pushdown(id);
        if(tree[id].l == l && tree[id].r == r){
            tree[id].lazy = 1 - tree[id].lazy;
            
            tree[id].sum = (tree[id].r - tree[id].l + 1) - tree[id].sum;
            return;
            }
        ll mid = (tree[id].l + tree[id].r) >> 1;
        if(mid < l){
            update(rid,l,r);
            }
        else if(mid >= r){
            update(lid,l,r);
            }
        else{
            update(lid,l,mid);
            update(rid,mid + 1,r);
            }
        tree[id].sum = tree[lid].sum + tree[rid].sum;
        }
        
    ll getsum(ll id,ll l,ll r){
        pushdown(id);
        if(tree[id].l == l && tree[id].r == r){
            return tree[id].sum;
            }
        ll mid = (tree[id].l + tree[id].r) >> 1;
        if(mid < l){
            return getsum(rid,l,r);
            }
        else if(mid >= r){
            return getsum(lid,l,r);
            }
        else{
            return getsum(lid,l,mid) + getsum(rid,mid + 1,r);
            }
        }
        
    int main(){
        ll num,nask;
        cin>>num>>nask;
        build(1,1,num);
        ll ask,x,y,v;
        for(ll i = 1;i <= nask;i++){
            cin>>ask;
            if(ask == 0){
                cin>>x>>y;
                update(1,x,y);
                }
            if(ask == 1){
                cin>>x>>y;
                cout<<getsum(1,x,y)<<endl;
                }
            }
        return 0;
        }
    
  • 相关阅读:
    v-bind 和v-model 的区别
    解决PC端和移动端自适应问题?
    安全解决将字符串" "转换成换行
    最全的正则表达式-匹配中英文、字母和数字(转)
    vue:style标签中的scoped属性(作用域)和lang属性的介绍
    vue项目main.js文件下import router from './router'默认导入router文件夹下index.js的原因
    VUE修改样式无效
    lodop如何获取打印机名称
    深拷贝和浅拷贝
    Vue.js学习笔记:props传递数据(转)
  • 原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9283231.html
Copyright © 2011-2022 走看看