线段树与树状数组

线段树与树状数组都是十分经典的数据结构,其实能用树状数组解决的问题也都能用线段树解决,但线段树相比于树状数组常数较大。
单点修改区间查询
线段树做法树状数组做法,其实单纯实现这个还是用树状数组较好(毕竟常数小还好写)
区间修改区间查询

  1. 只有区间加 树状数组做法线段树做法
  2. 既有区间加又有区间乘,应该只能用线段树做法了,注意要乘法优先(每次更改时先乘再加)。

权值线段树/树状数组
其实就是正常线段树/树状数组维护的定义域下标为正常下标,现在为权值。
扫描线
这是个很重要的东西,可以解决二维数点问题。就是将两个维度的偏序关系一个维排序,另一个维用树状数组维护。求矩形面积并要用线段树维护。(详见此题
不断插入区间,一边插入一边问给定[l,r],插入过的与[l,r]相交的区间有多少
答案其实就是:r之前的所有区间开头数(包括R)l之前的所有区间结尾数(不包括l),用线段树或树状数组维护前缀。
区间可以拆成左右两个端点维护。(


线段树专讲
线段树其实就是通过几个小区间的信息,合并出一个大区间的信息,所以线段树的运算要满足结合律。对不同题目,我们要考虑记录不同的信息,要合并它们。

  1. 动态开点线段树
    就是定义域下标范围太大,不能把整棵树开满,那就用一个点,看一个点
    code:
点击查看代码
inline void update(int &o,int l,int r,int x,int val){
  if(!o)o=++ncnt;//开点
  if(l==r){
      sum[o]+=val;return;
  }
  int mid=(l+r)>>1;
  if(x<=mid)update(lc[o],l,mid,x,val);
  else update(rc[o],mid+1,r,x,val);
  pushup(o);
}
int ask(int o,int l,int r,int L,int R){
  if(!o)return 0;//没这个点,直接返回0
  if(L<=l && R>=r)return sum[o];
  int val=0;
  int mid=(l+r)>>1;
  if(L<=mid)val+=ask(lc[o],l,mid,L,R);
  if(R>mid)val+=ask(rc[o],mid+1,r,L,R);//递归计算
  return val;
}
  1. 线段树二分,线段树上二分,听起来挺高深,其实就是像二分一样每次判断查询区间往右挪还是往左挪,然后想应该去的方向递归。
  2. 线段树维护区间最大子段和
    线段树每个点维护四个信息,区间最大子段和,最大前缀,最大后缀,区间和。用这几个条件合并(
  3. 结合树
    对一棵子树修改/查询,由于字数内dfs序是连续的,用线段树维护。(
  4. 区间修时取模操作
    由于每次取模至少减半,所以一个数最多被修改log次,区间修时可以递到每个l==r更改,复杂度log2(
  5. 当我们每次对区间增加一个等差数列,求单点,可以线段树维护差分数组。(
  6. 当区间修改是要排序,并且值域范围不大时,可以在线段树每个点统计每个值在这个区间出现多少次,每次要对某区间排序时要先取出每个值出现多少次,然后按AAA..BB...CC..的方法进行值域大小次区间改(比如将第一个阶段区间的A数量改变)

树状数组专讲
单点修矩阵查
二维其实与一维没什么区别,就是两个维跳lowbit而已和二位前缀和。
矩阵修改矩阵查询
这个还是用树状数组解比较舒服,其实就是树状数组的单点修矩阵查加上区间修区间查的差分思想,要推的式子见这里
逆序对
向前走时加一个权值树状数组,其实相当于二维数点的一维已经排好序了。

posted @   煦阳gyy  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示