本文来源于大佬的讲解:
1:线段树从0开始:https://blog.csdn.net/zearot/article/details/52280189
2:详解:https://www.cnblogs.com/AC-King/p/7789013.html(包含扫描线,可持久化(主席树))
综述
假设有编号从1到n的n个点,每个点都存了一些信息,用[L,R]表示下标从L到R的这些点。
线段树的用处就是,对编号连续的一些点进行修改或者统计操作,修改和统计的复杂度都是O(log2(n)).
线段树的原理,就是,将[1,n]分解成若干特定的子区间(数量不超过4*n),然后,将每个区间[L,R]都分解为
少量特定的子区间,通过对这些少量子区间的修改或者统计,来实现快速对[L,R]的修改或者统计。
由此看出,用线段树统计的东西,必须符合区间加法,否则,不可能通过分成的子区间来得到[L,R]的统计结果。
符合区间加法的例子:
数字之和——总数字之和 = 左区间数字之和 + 右区间数字之和
最大公因数(GCD)——总GCD = gcd( 左区间GCD , 右区间GCD );
最大值——总最大值=max(左区间最大值,右区间最大值)
不符合区间加法的例子:
众数——只知道左右区间的众数,没法求总区间的众数
01序列的最长连续零——只知道左右区间的最长连续零,没法知道总的最长连续零
一个问题,只要能化成对一些连续点的修改和统计问题,基本就可以用线段树来解决了,由于点的信息可以千变万化,所以线段树是一种非常灵活的数据结构,可以做的题的类型特别多,只要会转化。
线段树当然是可以维护线段信息的,因为线段信息也是可以转换成用点来表达的(每个点代表一条线段)。
模板1:
单点更新,单点查询:
int a[maxn];
int ans_min,ans_max,ans_sum;
struct node{
int l,r;
int maxx,minx,sum;
}tree[maxn<<2];
void build(int o,int l,int r)
{
tree[o].l = l;
tree[o].r = r;
if(l == r){
tree[o].maxx = tree[o].minx = tree[o].sum = a[l];
return ;
}
int m = MID(l,r);
int lc = lson(o),rc = rson(o);
build(lc,l,m);
build(rc,m+1,r);
tree[o].maxx = max(tree[lc].maxx, tree[rc].maxx);
tree[o].minx = min(tree[lc].minx, tree[rc].minx);
tree[o].sum = tree[lc].sum + tree[rc].sum;
}
void update(int o,int p,int v)
{
if(tree[o].l == tree[o].r){
tree[o].maxx = v; //视情况而定
tree[o].minx = v;
tree[o].sum = v;
return ;
}
int m = MID(tree[o].l,tree[o].r);
int lc = lson(o),rc = rson(o);
if(p<=m) update(lc,p,v);
else update(rc,p,v);
tree[o].maxx = max(tree[lc].maxx, tree[rc].maxx);
tree[o].minx = min(tree[lc].minx, tree[rc].minx);
tree[o].sum = tree[lc].sum + tree[rc].sum;
}
void query_init()//查询前,将全局变量初始化
{
ans_max = -INF;
ans_min = INF;
ans_sum = 0;
}
void query(int o,int l,int r)
{
if(tree[o].l == l && tree[o].r == r){
ans_max = max(ans_max, tree[o].maxx);
ans_min = min(ans_min, tree[o].minx);
ans_sum += tree[o].sum;
return;
}
int m = MID(tree[o].l,tree[o].r);
int lc = lson(o),rc = rson(o);
if(r<=m) query(lc,l,r);
else if(l>m) query(rc,l,r);
else{
query(lc,l,m);
query(rc,m+1,r);
}
}
模板2:
区间更新,区间查询:
LL a[maxn];
struct node{
int l,r;
LL sum,inc;
}tree[maxn<<2];
void build(int o,int l,int r)
{
tree[o].l = l;
tree[o].r = r;
tree[o].inc = 0;
if(l == r){
tree[o].sum = a[l];
return ;
}
int m = MID(l,r);
int lc = lson(o),rc = rson(o);
build(lc,l,m);
build(rc,m+1,r);
tree[o].sum = tree[lc].sum+tree[rc].sum;
}
void update(int o,int l,int r,LL val)
{
if(tree[o].l == l && tree[o].r == r){
tree[o].inc += val;
return ;
}
tree[o].sum += val*(r-l+1);
int m = MID(tree[o].l,tree[o].r);
int lc = lson(o),rc = rson(o);
if(r<=m) update(lc,l,r,val);
else if(l>m) update(rc,l,r,val);
else{
update(lc,l,m,val);
update(rc,m+1,r,val);
}
}
LL ans_sum;
LL query(int o,int l,int r)
{
if(tree[o].l == l && tree[o].r == r){
return tree[o].sum + tree[o].inc *(r-l+1);
}
tree[o].sum += (tree[o].r-tree[o].l+1)*tree[o].inc;
int m = MID(tree[o].l,tree[o].r);
int lc = lson(o),rc = rson(o);
update(lc,tree[o].l,m,tree[o].inc);
update(rc,m+1,tree[o].r,tree[o].inc); //只更新到下一层
tree[o].inc = 0; //清除标记
if(r<=m) ans_sum = query(lc,l,r);
else if(l>m) ans_sum = query(rc,l,r);
else{
ans_sum = query(lc,l,m)+query(rc,m+1,r);
}
return ans_sum;
}