首页 > 其他分享 >线段树

线段树

时间:2023-07-10 15:57:06浏览次数:27  
标签:pr int 线段 mid tag 区间 pl

代码思路

主体部分:

初始化,修改,查询
(即build,update,query三个函数)

辅助部分:

区间值维护,打懒标记,消懒标记
(即push_up,add_tag,push_down三个函数)

简化部分:

自定义数据类型,左右儿子自助计算
(struct Tree,ls&rs函数)


原理(极简)

树+分块=线段树,无尽的二分


代码注释

#include <bits/stdc++.h>
using namespace std;
#define ll long long
// 区间修改,区间查询,区间和 
const int N = 4e6+5; // 数据范围,建议x4,多开不上当,多开不吃亏
ll a[N], tree[N], tag[N];

int ls(int p){return p << 1;} // 左儿子
int rs(int p){return p<<1|1;} // 右儿子

void push_up(int p){
    tree[p] = tree[ls(p)] + tree[rs(p)];
} // 维护区间值(运算可根据题意变动,如求最大值等)

void build(int p, int pl, int pr){ // 初始化
    tag[p] = 0; // 懒运算标记(lazy_tag)
    if(pl == pr){tree[p] = a[pl]; return ;}
    // 若已经走到了最底层,则根据原数列赋值
    int mid = (pl+pr)>>1; // 将当前区间“平均地”分为两块
    build(ls(p), pl, mid); // 左
    build(rs(p), mid+1, pr); // 右
    push_up(p); // 计算区间值
}

void add_tag(int p, int pl, int pr, ll d){
    tag[p] += d; // 更新懒运算标记(add_lazy_tag)
    tree[p] += d*(pr-pl+1); // 更新区间值,记得要算区间内所有数的变动
}

void push_down(int p, int pl, int pr){ // 把懒标记转移到子树
    if(!tag[p]) return ;
    int mid = (pl+pr)>>1; // 一分为二(梅开二度)
    add_tag(ls(p), pl, mid, tag[p]); // 左
    add_tag(rs(p), mid+1, pr, tag[p]); // 右
    tag[p] = 0; // 消除标记
}

void update(int l, int r, int p, int pl, int pr, ll d){ // 区间修改([l,r]+d)
    if(l <= pl && r >= pr){ // 如果被完全覆盖
        add_tag(p, pl, pr, d); // 打上懒标记(等到之后不能完全覆盖的时候用)
        return ; // 不用跑子树
    }
    push_down(p, pl, pr); // 不能完全覆盖:先把懒标记传下去(懒标记一定是完全覆盖的)
    int mid = (pl+pr)>>1; // 一分为二(梅花三弄)
    if(l <= mid) update(l, r, ls(p), pl, mid, d); // 如果左边被(部分)覆盖
    if(r > mid) update(l, r, rs(p), mid+1, pr, d); // 如果右边被(部分)覆盖
    push_up(p); // 子树已经更新完,用儿子的值维护当前区间值
}

ll query(int l, int r, int p, int pl, int pr){ // 区间查询([l,r])
    if(l <= pl && r >= pr) return tree[p]; // 如果被完全覆盖:返回区间值
    push_down(p, pl, pr); // 不能完全覆盖,:为了保证值是最新的,传递懒标记
    ll ret = 0; // 返回值:统计左、右区间被覆盖部分合并后的值
    int mid = (pl+pr)>>1; // 一分为二(大四喜)
    if(l <= mid) ret += query(l, r, ls(p), pl, mid); // 如果左边被(部分)覆盖
    if(r > mid) ret += query(l, r, rs(p), mid+1, pr); // 如果右边被(部分)覆盖
    return ret; // 返回答案
}

int main(){
    int n, m; // 原数列长为n,对其有m次操作
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
        scanf("%lld", &a[i]); // 输入原数列

    build(1, 1, n); // 建树(根节点编号为1,代表了[1,n]的区间范围)

    for(int i = 1; i <= m; i++){
        int type, l, r; ll d;
        scanf("%d", &type); // 操作类别
        if(type == 1){ // type[1]:修改区间[l,r]中的数,使其值加d
            scanf("%d%d%lld", &l, &r, &d);
            update(l, r, 1, 1, n, d);
        }
        if(type == 2){ // type[2]:输出区间[l,r]的区间和
            scanf("%d%d", &l, &r);
            printf("%lld\n", query(l, r, 1, 1, n));
        }
    }
    return 0;
}

标签:pr,int,线段,mid,tag,区间,pl
From: https://www.cnblogs.com/meteor2008/p/17541232.html

相关文章

  • 线段树学习笔记与总结
    线段树学习笔记与总结目录线段树引入资源链接模板线段树引入我们经常会遇到需要维护一个序列的问题,例如给定一个整数序列,每次操作会修改序列某个位置上的数,或是海间你序列巾某个区问内所有数的和,用“暴力"算法,单点修改的复杂度为\(O(1)\),询问区间和的单次复杂度为\(O(N)\)......
  • 【学习笔记】李超线段树
    维护一次函数以模板题为例。使用线段树维护线段,每个节点维护的都是完全覆盖这个区间的线段。考虑当前节点已经有线段\(f\),现在加入线段\(g\)。暴力想法是暴力递归每个子区间,把更优的保留,注意到\(f,g\)最多一个交点,因此也最多一侧的子区间需要暴力递归。具体流程如下:先......
  • abc309f <线段树 + 离散化 + 双指针>
    F-BoxinBox//https://atcoder.jp/contests/abc309/tasks/abc309_f//<线段树+离散化+双指针>[unique+lower_bound+erase+lambda+vector]//总体思路:将每个三元组记录为如a[3]的3维向量,依次考虑每个向量,检查是否存在一个向量完全比它'小'//将向量按......
  • 线段上的格点数量
    平面坐标系上有两个格点\(p_1(x_1,y_1)\)和\(p_2(x_2,y_2)\),求线段\(p_1p_2\)上除了\(p_1,p_2\)还有几个格点。结论当斜率存在时,格点数量为\(gcd(|y_2-y_1|,|x_2-x_1|)-1\)当斜率不存在且\(y_1\ney_2\)时,格点数量为\(|y_2-y_1|-1\)当斜率不存在且\(y_1=y_2\)时,格点数量为0......
  • CF1842E Tenzing and Triangle - 线段树优化 dp -
    题目链接:https://codeforces.com/contest/1842/problem/E题解:首先,如果两个等腰三角形相交了,那答案肯定不会更优。因此不会相交。先考虑一个\(n^2\)的dp:设\(dp_i\)表示考虑到\(x=i\)时的最小代价,首先可以先都加一个\(\sumc_i\),这样只需要考虑三角形覆盖范围内的\(c_i......
  • BZOJ 3073: [Pa2011]Journeys 线段树优化最短路
    3073:[Pa2011]JourneysTimeLimit: 20Sec  MemoryLimit: 512MBSubmit: 243  Solved: 80[Submit][Status][Discuss]DescriptionSeter建造了一个很大的星球,他准备建造N个国家和无数双向道路。N个国家很快建造好了,用1..N编号,但是他发现道路实在太多了,他要一条条......
  • BZOJ 2243: [SDOI2011]染色 树链剖分+线段树
    2243:[SDOI2011]染色TimeLimit: 20Sec  MemoryLimit: 512MBSubmit: 7623  Solved: 2855[Submit][Status][Discuss]Description给定一棵有n个节点的无根树和m个操作,操作有2类:1、将节点a到节点b路径上所有点都染成颜色c;2、询问节点a到节点b路径上的颜色段数量(连续......
  • 李超线段树模板
    细节和理解详见注释题目:https://www.luogu.com.cn/problem/P4097#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongconstintmod1=39989;constintmod2=1e9;constdoubleeps=1e-9;typedefpair<double,int>pdi;intlasans;//细节://注意42排,开始......
  • 【线段树】 POJ 3667 Hotel
    这道题和那道HDOJ3308LCIS比较像。。那道题目我就写了超久,当时是不确定保存什么信息。。这道题也写了很久,主要是各种低级错误,找错找了很久,超级火。。。。#include<iostream>#include<sstream>#include<algorithm>#include<vector>#include<queue>#include<stack>#inc......
  • 【线段树】 HDOJ 4027 Can you answer these queries?
    想了好久的线段树,用到的思想好巧妙,因为最大是2的63次方,所以开了个6,7次的平方就全变成一了。。。。比较好写的一种方法是直接用不加lazy的线段树更新区间,然后加一个当sum=R-L+1就不更新的剪枝。。。。我的代码是每加一次开根就pushdown,达到7次以后就不更新了。。。#include<iost......