首页 > 其他分享 >线段树

线段树

时间:2024-07-20 16:57:59浏览次数:13  
标签:return 线段 mid dat flag 区间 LL

把给定的区间转换成如图所示的一棵二叉树

每次把区间一分为 2, 左边是左儿子, 右边是右儿子

对于每个节点的信息, 都可以由两个儿子的信息得到

如何单点查询/修改

可以发现, 两个儿子处理的区间没有交集, 所以每次只要判断是在左儿子还是在右儿子, 不断的递归

对于区间查询, 每一次记录修改后的答案, 如果查询区间完全包含找到的节点, 直接返回答案

时间复杂度

我们先把所以节点放到最下面一层, 一次往上合并, 我们发现, 查询的区间如果在同一层一定是连续的, 而且如果有一层有 3 个节点, 其中一定有两个可以合并到上一层, 又最多只有 \(\lceil \log_2 \rceil\) 层

对于区间修改, 我们要用到懒标记, 如果修改区间完全包含这个区间, 给节点打上一个标记, 每次要用到时再下传, 时间复杂度与上面类似

时间复杂度 \(O(n \log n)\)
空间复杂度 \(O(n)\)

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;

using LL = long long;

LL n, m, a[N], l, r, k, op;

struct TREE{
  struct Node{
    LL l, r, dat, flag = 0;
  }t[4 * N];
  void dfs(LL p, LL l, LL r){
    t[p].l = l, t[p].r = r;
    if(l == r){
      t[p].dat = a[l];
      return;
    }
    LL mid = (l + r) >> 1;
    dfs(p * 2, l, mid);
    dfs(p * 2 + 1, mid + 1, r);
    t[p].dat = t[p * 2].dat + t[p * 2 + 1].dat;
  }
  void DFS(LL p, LL l, LL r, LL k){
    if(l <= t[p].l && r >= t[p].r){
      t[p].dat += (t[p].r - t[p].l + 1) * k, t[p].flag += k;
      return;
    }
    if(t[p].flag){
      t[p * 2].dat += (t[p * 2].r - t[p * 2].l + 1) * t[p].flag;
      t[p * 2 + 1].dat += (t[p * 2 + 1].r - t[p * 2 + 1].l + 1) * t[p].flag;
      t[p * 2].flag += t[p].flag, t[p * 2 + 1].flag += t[p].flag;
      t[p].flag = 0;
    }
    int mid = (t[p].l + t[p].r) >> 1;
    if(l <= mid){
      DFS(p * 2, l, r, k);
    }
    if(r > mid){
      DFS(p * 2 + 1, l, r, k);
    }
    t[p].dat = t[p * 2].dat + t[p * 2 + 1].dat;
  }
  LL SUM(LL p, LL l, LL r){
    if(l <= t[p].l && r >= t[p].r){
      return t[p].dat;
    }
    if(t[p].flag){
      t[p * 2].dat += (t[p * 2].r - t[p * 2].l + 1) * t[p].flag;
      t[p * 2 + 1].dat += (t[p * 2 + 1].r - t[p * 2 + 1].l + 1) * t[p].flag;
      t[p * 2].flag += t[p].flag, t[p * 2 + 1].flag += t[p].flag;
      t[p].flag = 0;
    }
    LL sum = 0;
    int mid = (t[p].l + t[p].r) >> 1;
    if(l <= mid){
      sum += SUM(p * 2, l, r);
    }
    if(r > mid){
      sum += SUM(p * 2 + 1, l, r);
    }
    t[p].dat = t[p * 2].dat + t[p * 2 + 1].dat;
    return sum;
  }
}tree;

int main(){
  cin >> n >> m;
  for(LL i = 1; i <= n; ++i){
    cin >> a[i];
  }
  tree.dfs(1, 1, n);
  for(LL i = 1; i <= m; ++i){
    cin >> op;
    if(op == 1){
      cin >> l >> r >> k;
      tree.DFS(1, l, r, k);
    }
    else{
      cin >> l >> r;
      cout << tree.SUM(1, l, r) << '\n';
    }
  }
  return 0;
}

标签:return,线段,mid,dat,flag,区间,LL
From: https://www.cnblogs.com/liuyichen0401/p/18313363

相关文章

  • 线段树(原理、构造和区间查询,例题:Balanced Lineup)
    概念原理    线段树是分治法和二叉树的结合,二叉树上的节点都是根据分治得到的。节点所表示的,也就是线段,可以是区间和、最值或者是其他的,,每次分治,左右子树各一半,每个节点的值代表了以它为根的子树上所有节点的值。通过线段树,大区间的解可以从小区间的解合并而来。构......
  • 线段树优化建图
    为什么?什么时候用线段树优化建图例题如果此时暴力建边\(O(n^{2})\)肯定会TLE观察到题目中的“区间”此时考虑用线段树优化建图,在每个区间上连边(线段树上只有\(\log{n}\)个区间)来减少边的个数实现方法?摘抄自tzx_wk我们就拿\(2\)操作来举例吧。现在假设假如有一个点......
  • 关于线段树优化建图
    线段树优化建图引入对于这道板子题但是我不会大概意思就是:有\(n\)个点、\(q\)次操作。每一种操作为以下三种类型中的一种:连一条\(u→v\)的有向边,权值为w对于所有\(i∈[l,r]\)连一条\(u→i\)的有向边,权值为\(w\)对于所有\(i∈[l,r]\)连一条\(i→u\)的......
  • Legacy(线段树优化建图)
    CF786B-Legacy线段树优化建图板子题。。。。。。暴力建边\(\mathcalO(n^2)\))肯定会\(TLE\)但仔细分析可以发现,题面中有一个我们非常熟悉的字眼“区间”,这启示我们,可不可以以此作为解题的突破口呢?答案是肯定的。想到区间我们可以联想到各种我们很熟悉的\(trick\),如前缀和、......
  • 线段树优化建图
    线段树优化建图用途:处理区间连边做法:建两颗线段树,一颗处理区间的入边,另一颗处理出边(如果用一颗线段树的话,边权就都为0了)例题:Legacy板子题,直接看代码点此查看代码#include<bits/stdc++.h>#include<bits/extc++.h>//usingnamespace__gnu_pbds;//usingnamespa......
  • P3373 【模板】线段树 2(区间乘+加操作,先乘后加原则)
    题目来源:https://www.luogu.com.cn/problem/P3373//题意:对区间[l,r]可以乘法,加法操作,查询和操作。//思路:既有乘法又有加法,肯定是要有两个标记。纯加法和纯乘法操作是很简单的,但是既有乘法又有加法涉及到先乘后加和先加后乘的顺序。//////所以现在是如何将先加后成也可以......
  • 【数据结构】- 线段树
    前言线段树用于维护区间上的值,可以在$O(\logn)$的时间复杂度内实现单点,区间修改及查询。并且所有满足结合律的运算都可以用线段树进行加速。基本操作建树给你长度为$n$的正整数序列$A$,要你实现单点修改与区间查询。(加和)这就是线段树的基本题目。线段树,字面上来看就是把......
  • 基础线段树相关
    权值线段树线段树在这里作为前置知识,我们就不说了,而且权值线段树也不是核心内容,不会大篇幅讲。首先,权值线段树在维护什么?维护的是桶。然后,权值线段树有什么用?可以求一些序列的第\(k\)大之类的问题。于是我们放个板子题。第k小整数简单题,直接看代码和注释就行,当然也可以......
  • 2021 ICPC 网络赛 第二场 L Euler Function(势能线段树,欧拉函数,状态压缩)
    2021ICPC网络赛第二场LEulerFunction题意给定序列,定义两个操作\(l,r,x\)对区间\([l,r]\)的数乘\(x\)\(l,r\)求\(\sum\phi{a}_{i}\)思路注意欧拉函数的性质,若\(i\bmodp=0\),\(\phi(i*p)=p*\phi(i)\),否则\(\phi(i*p)=(p-1)*\phi(i)\)因为\(x,w\)的......
  • 李超线段树
    李超线段树用来维护线段(一次函数)信息。值域线段树:对于值域线段树维护\(x\)轴上的区间\([l,r]\),维护\(s\),表示在\(x=mid\)处可能取最大值的线段(不一定就是最大)。添加操作:新边\(u\),旧边\(v\)。1.将边拆为最多\(\log\)个在线段树上的线段。2.如果\(u\)与\(v\)存在完全覆盖关......