模版:线段树
Posted 24Kmagic
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模版:线段树相关的知识,希望对你有一定的参考价值。
基本思想
线段树,是一种二叉搜索树(即每个结点最多有两棵子树,通常叫做左右子树)。
它将一段区间划分为若干单位区间,每一个节点都储存着一段区间[L,R]的信息,其中叶子节点L=R。它的大致思想是:将区间[1,n]平均分成2个小区间,再将小区间平均分成2个更小区间…以此类推,直到区间长度变为1无法分成两个更小的区间。通过对这些小区间进行修改、查询,来实现对大区间的修改、查询。
用线段树维护的问题必须满足可以用两个小区间的信息合并成大区间信息,否则是不可能将大问题划分成子问题来解决的。
模版
1、建树
void build(int k,int l,int r){
if(l ==r) {
sum[k] = f[l];
return;
}
int mid = (l + r) >> 1;
build(2 * k,l,mid);
build(2 * k + 1,mid + 1,r);
sum[k] = sum[2 * k] + sum[2 * k + 1];
}
2、单点修改
void dotchange(int k,int l,int r,int x,int v){
if(l == r){
sum[k] +;
return;
}
int mid(l + r) / 2;
if(x <= mid) dotchange(2 * k,l,mid,x,v);
else dotchange(2 * k + 1,mid + 1,r,x,v);
sum[k] = sum[2 * k] + sum[2 * k + 1];
}
3、区间修改&&区间查询
说明 : 区间修改中,如果我们按照正常的思路修改的话,我们会发现我们对n个叶节点进行了修改,这时的复杂度是非常高的,所以我们可以用到懒标记,如果下面的一大段区间都需要修改,我们直接用懒标记标记,然后return,这样可以通过节省操作次数省一些复杂度
PS:懒标记与下放:
void Add(int k,int l,int r,int v){ //所以具体来说我们的懒标记 并不会修改叶节点的值,而只会一步到位修改我们到达的父节点的值
add[k] += v; //打懒标记
sum[k] += (r - l + 1) * v; //更新节点值(所有叶节点)
return;
}
void pushdown(int k,int l,int r,int mid){ //等我们需要用到叶节点的值的时候我们再通过pushdown将懒标记下方,然后一层一层更新节点值
if(add[k] == 0) return;
Add(2 * k,l,mid,add[k]);
Add(2 * k + 1,mid + 1,r,add[k]);
add[k] = 0;
}
区间修改,区间查询代码:
void longchange(int k,int l,int r,int x,int y,int v){
if(x <= l && r <= y){
Add(k,l,r,mid);
return;
}
int mid = (l + r) >> 1;
pushdown(k,l,r,mid);
if(x <= mid) longchange(2 * k,l,mid,x,y,v);
if(y >= mid + 1) longchange(2 * k + 1,mid + 1,r,x,y,v);
sum[k] = sum[2 * k] + sum[2 * k + 1];
}
int longquery(int k,itn l,int r,int x,int y){
if(x <= l && r <= y) return sum[k];
int mid = (l + r) >> 1;
pushdown(k,l,r,mid);
int res = 0;
if(x <= mid) res += longquery(2 * k,l,mid,x,y);
if(y >= mid + 1) res += longquety(2 * k + 1,mid + 1,r,x,y);
return res;
}
结语:
综上来看,线段树主要是用了分治的思想。同时线段树还有一些变型,以上只是最基本的模版,要是想搞懂其他的变型,还需要好好背模版!
以上是关于模版:线段树的主要内容,如果未能解决你的问题,请参考以下文章