浅谈线段树(by Shine_hale)

一. 线段树是什么?

线段树,顾名思义,就是将区间变成线段进行处理
如图可以看出,将1-10这个线段不断拆分,进而得到子节点;

摘自互联网

二、为什么要用线段树

线段树修改简单,方便快捷,同时;在查询上可以使时间复杂度到达O(1),这很厉害了
同时不同于RMQ问题,可以在线进行修改,不用花时间进行重构。
但是线段树有个缺点有个较大的常数。
话不多说,正式开始介绍,hale理解下的线段树大法

线段树模板

线段树,毕竟是树形结构,第一步当然是建树了
首先要有准备工作,处理左儿子,右儿子的问题

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

建树过程(递归建树)其实还有一种建树方法

zkw线段树 (但是我不会告诉你我不会的)嘤嘤嘤

void build(int p,int l,int r)
{ int mid=(l+r)>>1;
  if (l==r) { st[p].ans=a[l];return;}
  build(ls(p),l,mid);
  build(rs(p),mid+1,r);
  push_up(p); 
}

其实push_up本身就是向上回溯的过程
我只是把这个过程单独列了出来

void push_up(int p)
{ st[p].ans=st[ls(p)].ans+st[rs(p)].ans;}

接着介绍如何区间修改
为什么不介绍单点修改,其实很简单了,单点不就可以看成一个只包含自己的区间吗
那么对于区间操作,我们考虑引入一个名叫“lazylazy tagtag”(懒标记)的东西——之所以称其“lazylazy”,是因为原本区间修改需要通过先改变叶子节点的值,然后不断地向上递归修改祖先节点直至到达根节点,时间复杂度最高可以到达O(nlogn)O(nlogn)的级别。但当我们引入了懒标记之后,区间更新的期望复杂度就降到了O(logn)O(logn)的级别且甚至会更低.

void push_down(int p,int l,int r)
{ int mid=(l+r)>>1;
  st[ls(p)].add+=st[p].add;
  st[rs(p)].add+=st[p].add;
  st[ls(p)].ans+=st[p].add*(mid-l+1);
  st[rs(p)].ans+=st[p].add*(r-mid);
  st[p].add=0;
}

首先,懒标记的作用是记录每次、每个节点要更新的值,也就是delta,但线段树的优点不在于全记录(全记录依然很慢qwq),而在于传递式记录;

整个区间都被操作,记录在公共祖先节点上;只修改了一部分,那么就记录在这部分的公共祖先上;如果四环以内只修改了自己的话,那就只改变自己。

如果我们采用上述的优化方式的话,我们就需要在每次区间的查询修改时pushdown一次,以免重复或者冲突或者爆炸qwqqwq
那么对于pushdown而言,其实就是纯粹的pushup的逆向思维(但不是逆向操作): 因为修改信息存在父节点上,所以要由父节点向下传导lazy tag
那么问题来了:怎么传导pushdown呢?这里很有意思,开始回溯时执行pushup,因为是向上传导信息;那我们如果要让它向下更新,就调整顺序,在向下递归的时候pushdown就好了

void update(int nl,int nr,int l,int r,int p,int k)
{ if (nl<=l&&nr>=r) 
  { st[p].ans+=k*(r-l+1);
    st[p].add+=k;return;}
  int mid=(l+r)>>1;
  push_down(p,l,r);
  if (nl<=mid) update(nl,nr,l,mid,ls(p),k);
  if (nr>mid) update(nl,nr,mid+1,r,rs(p),k);
  push_up(p);
}

好了,区间修改了之后就该区间求和了
思想跟上面的差不多,大家自行理解就好了、

int query(int qx,int qy,int l,int r,int p)
{ int res=0;
  if (qx<=l&&qy>=r) return st[p].ans;
  push_down(p,l,r);
  int mid=(l+r)>>1;
  if (qx<=mid) res+=query(qx,qy,l,mid,ls(p));
  if (qy>mid) res+=query(qx,qy,mid+1,r,rs(p));
  return res;
}

最后插一句
区间乘法的维护需要注意先后顺序同时要注意取模,一定要一步一取模啊

void push_down(int p,int l,int r)
{ int mid=(l+r)>>1;
  st[ls(p)].nul=(st[ls(p)].nul*st[p].nul)%Mod;
  st[rs(p)].nul=(st[rs(p)].nul*st[p].nul)%Mod;
  st[ls(p)].add=(st[ls(p)].add*st[p].nul+st[p].add)%Mod;
  st[rs(p)].add=(st[rs(p)].add*st[p].nul+st[p].add)%Mod;
  st[ls(p)].ans=(st[ls(p)].ans*st[p].nul+st[p].add*(mid-l+1))%Mod;
  st[rs(p)].ans=(st[rs(p)].ans*st[p].nul+st[p].add*(r-mid))%Mod;
  st[p].nul=1;
  st[p].add=0;
}

最后的最后,我知道大家不想听我扯这么多

所以直接暴力上代码吧,嘤嘤嘤

#include<bits/stdc++.h>
using namespace std;
const int M=200010;
int m,n,k,p,a[M];
struct node
{ int ans;
  int add;
};
node st[M<<2];
int ls(int p) {return p<<1;}
int rs(int p) {return p<<1|1;}
void push_up(int p)
{ st[p].ans=st[ls(p)].ans+st[rs(p)].ans;}
void build(int p,int l,int r)
{ int mid=(l+r)>>1;
  if (l==r) { st[p].ans=a[l];return;}
  build(ls(p),l,mid);
  build(rs(p),mid+1,r);
  push_up(p); 
}
void push_down(int p,int l,int r)
{ int mid=(l+r)>>1;
  st[ls(p)].add+=st[p].add;
  st[rs(p)].add+=st[p].add;
  st[ls(p)].ans+=st[p].add*(mid-l+1);
  st[rs(p)].ans+=st[p].add*(r-mid);
  st[p].add=0;
}
void update(int nl,int nr,int l,int r,int p,int k)
{ if (nl<=l&&nr>=r) 
  { st[p].ans+=k*(r-l+1);
    st[p].add+=k;return;}
  int mid=(l+r)>>1;
  push_down(p,l,r);
  if (nl<=mid) update(nl,nr,l,mid,ls(p),k);
  if (nr>mid) update(nl,nr,mid+1,r,rs(p),k);
  push_up(p);
}
int query(int qx,int qy,int l,int r,int p)
{ int res=0;
  if (qx<=l&&qy>=r) return st[p].ans;
  push_down(p,l,r);
  int mid=(l+r)>>1;
  if (qx<=mid) res+=query(qx,qy,l,mid,ls(p));
  if (qy>mid) res+=query(qx,qy,mid+1,r,rs(p));
  return res;
}
int main()
{ int x,y,z;
  scanf("%d",&n);
  for (int i=1;i<=n;i++)
  scanf("%d",&a[i]);
  build(1,1,n);
  scanf("%d",&m);
  for (int i=1;i<=m;i++)
  { scanf("%d",&k);  
    switch(k)
    { case 1:{scanf("%d%d%d",&x,&y,&z);
              update(x,y,1,n,1,z);
              break;}
      case 2:{scanf("%d%d",&x,&y);
              printf("%d\n",query(x,y,1,n,1));
              break;}
    }
  }
  return 0;
}

hale还没学会别的高级操作,所以先就此搁笔了,愿大家越来越强

posted @ 2018-12-27 17:39  Shine_hale  阅读(233)  评论(0编辑  收藏  举报
Live2D