OI模板——线段树(segment_tree)O(lg n)

咱家越来越懒了,连解释都懒得去写了,主要思想就是把一个区间当作一个线段,然后不断的递归二分最后成为各个点,关键在于lazy_tag,当你正在处理的区间完全在目标操作区间时就可以用lazy来处理,不再递归下去,思想是这样的,想要会的话呢,应该先去照着打两遍,边打边理解,然后就可以自己打啦

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>

#define l(x) st[x].l
#define r(x) st[x].r
#define sum(x) st[x].sum
#define lazy(x) st[x].lazy

using namespace std;

typedef long long ll;

const int Size = 1e5 + 5;

struct segment_tree{
    int l,r;
    ll sum,lazy;
}st[Size << 2];

int a[Size];

void build(int,int,int);
void spread(int);
void change(int,int,int,int);
ll ask(int,int,int);

int main(){
    int n, q;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    build(1, 1, n);
    scanf("%d",&q);
    for (int i = 0; i < q; i++){
        int o,a, b, c;
        scanf("%d%d%d",&o,&a,&b);
        if (o==1){
            scanf("%d", &c);
            change(1, a, b, c);
        }
        else    cout << ask(1,a,b) << endl;//printf("%I64d\n", ask(1, a, b));
    }
    return 0;
} 

void build(int x,int l,int r){
    l(x) = l,r(x) = r;
    if (l == r){
        sum(x) = a[l];
        return ;
    }
    int mid = l + r >> 1;
    build(x << 1,l,mid);
    build(x << 1| 1,mid + 1,r);
    sum(x) = sum(x << 1) + sum(x << 1 | 1);
}

void spread(int x){
    if (lazy(x)){
        sum(x << 1) += lazy(x) * (r(x << 1) - l(x << 1) + 1);
        sum(x << 1 | 1) += lazy(x) * (r(x << 1 | 1) - l(x << 1 | 1) + 1);
        lazy(x << 1) += lazy(x);
        lazy(x << 1 | 1) += lazy(x);
        lazy(x) = 0;
    }
}

void change(int x,int l,int r,int delta){
    if (l <= l(x) && r >= r(x)){
        sum(x) += delta * (r(x) - l(x) + 1);
        lazy(x) += delta;
        return ;
    }
    int mid = l(x) + r(x) >> 1;
    spread(x);
    if (l <= mid)     change(x << 1,l,r,delta);
    if (r > mid)    change(x << 1 | 1,l,r,delta);
    sum(x) = sum(x << 1) + sum(x << 1 | 1);
    return ;
}

ll ask(int x,int l,int r){
    if (l <= l(x) && r >= r(x))
        return sum(x);
    int mid = l(x) + r(x) >> 1;
    ll ans = 0;
    if (l <= mid)    ans += ask(x << 1,l,r);
    if (r > mid)    ans += ask(x << 1 | 1,l,r);
    return ans;
}
时间: 2024-11-06 12:05:25

OI模板——线段树(segment_tree)O(lg n)的相关文章

算法模板——线段树1(区间加法+区间求和)

实现功能——1:区间加法:2:区间求和 最基础最经典的线段树模板.由于这里面操作无顺序之分,所以不需要向下pushup,直接累积即可 1 var 2 i,j,k,l,m,n,a1,a2,a3,a4:longint; 3 a,b:array[0..100000] of longint; 4 function max(x,y:longint):longint;inline; 5 begin 6 if x>y then max:=x else max:=y; 7 end; 8 function min

算法模板——线段树5(区间开根+区间求和)

实现功能——1:区间开根:2:区间求和(此模板以BZOJ3038为例) 作为一个非常规的线段树操作,其tag也比较特殊呵呵哒 1 var 2 i,j,k,l,m,n:longint; 3 a,b:array[0..500000] of int64; 4 function max(x,y:longint):longint;inline; 5 begin 6 if x>y then max:=x else max:=y; 7 end; 8 function min(x,y:longint):long

【线段树】【P3372】模板-线段树

百度百科 Definition&Solution 线段树是一种log级别的树形结构,可以处理区间修改以及区间查询问题.期望情况下,复杂度为O(nlogn). 核心思想见百度百科,线段树即将每个线段分成左右两个线段做左右子树.一个线段没有子树,当且仅当线段表示的区间为[a,a]. 由于编号为k的节点的子节点为2k以及2k+1,线段树可以快速的递归左右叶节点. lazy标记:当进行区间修改的时候,如果一个区间整体全部被包含于要修改的区间,则可以将该区间的值修改后,将lazy标记打在区间上,不再递归左

CSU-ACM集训-模板-线段树进阶

A题 原CF 438D The Child and Sequence 题意 给一串数字,m次操作,1.区间查询:2.区间取模:3.单点修改 基本思路 考虑到模如果大于区间的最大值,则取模没有意义.若小于则向下查询并修改,考虑到一个数每次取模最多为原数的\(1/2\),则可认为修改次数不超过\(\log{2}{n}\) 时间复杂度为\(O(n\log{2}{n}\log{2}{n})\) #include<bits/stdc++.h> #define FOR(i,a,b) for(int i=a

模板 - 线段树

线段树还需要模板的菜鸡 #include<bits/stdc++.h> using namespace std; typedef long long ll; #define lt ls, l, m #define rt rs, m + 1, r #define ls (o<<1) #define rs (o<<1|1) const int MAXM = 100000 + 5; ll a[MAXM]; ll st[MAXM * 4], lazy[MAXM * 4]; in

算法模板——线段树4(区间加+区间乘+区间覆盖值+区间求和)

实现功能——1:区间加法 2:区间乘法 3:区间覆盖值 4:区间求和 这是个四种常见线段树功能的集合版哦...么么哒(其实只要协调好三种tag的关系并不算太难——前提是想明白了线段树的工作模式) 代码长度几经修改后也大为缩水 还有!!!——通过BZOJ1798反复的尝试,我的出来一个重要结论——尽量减少pushup操作的不必要使用次数,对于程序提速有明显的效果!!! 1 type vet=record 2 a0,a1:longint; 3 end; 4 var 5 i,j,k,l,m,n,a1,

算法模板——线段树7(骰子翻转问题)

实现功能:首先输入一个长度为N的序列,由1-4组成(1表示向前滚一下,2表示向后滚一下,3表示向左滚一下,4表示向右滚一下,骰子原始状态:上1前2左4右5后3下6),然后输入任意多个操作,输入“1 x y”表示将序列第x个数改成y,输入“2 x y”表示输出对于原始状态的骰子,按照从x到y的序列操作可以使骰子变成什么样子 原理:还是线段树,而且只需要点修改区间访问,不过这里面区间之间的合并不再是简单的累加了,而是——置换关系,通过置换关系的合并实现及时的维护即可 1 type 2 cube=ar

模板——线段树

一颗最简单的线段树orz...但是感觉还是拍得好麻烦... 只支持区间加和区间查询 #include<iostream> #include<cstdlib> #include<cstdio> #include<algorithm> using namespace std; int a[2000010],n,m; typedef long long ll; struct inlinetree{ int l,r; ll s,lazy; }lt[34000100]

模板 线段树的区间修改

线段树的区间修改 区间绝对标记 改成同一个数 注意打标记前 要先判断 是否有标记 这道题不能像加法标记一样 标记初始化为 0 如果这道题 可以将数变成 0 那么0 就不能为初始值了 然后我们初始值要选择一个不会被干扰到的数字 比如 -1 就不会变成 -1 另外还要注意在标记清空时 要将标记 变成 -1 而不是 0 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstr