第一版
关于线段树本身
- 区间查询,单点修改
- 单点查询,区间修改
- lazy思想
关于例题
- 有一个LG的模板
我在恍惚之间又想起了那件事,仿佛就在昨天。
学校,午饭后,两个人在校园里闲逛,迎面遇上yjx,说:"你们是两个人,真好。我谔谔。
多说无益,从此,便是陌路人。总是无中生有,又有什么意思。
正文
我们先来了解一下线段树
定义
度娘上是这么说的 : 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
限制:此处以询问区间和为例。实际上线段树可以处理很多符合结合律的操作。(比如说加法,a[1]+a[2]+a[3]+a[4]=(a[1]+a[2])+(a[3]+a[4]))
线段树之所以称为“树”,是因为其具有树的结构特性。
就以一个图片为例,你建出来的线段树大概是这样的
表示
对于每一个子节点而言,都表示整个序列中的一段子区间;对于每个叶子节点而言,都表示序列中的单个元素信息;子节点不断向自己的父亲节点传递信息,而父节点存储的信息则是他的每一个子节点信息的整合。
我们来一波逐步肢解线段树
线段树的实现
节点表示与儿子节点
typedef long long ll;
#define fake ll
struct TreeNode{
fake mul_lazy,add_lazy,sum,l,r;
//l表示区间左端点,同理有r,mul_lazy表示乘的懒惰标记,同理有add_lazy,sum表示区间和
}tree[Maxn*4];
fake ls(fake k){return k<<1;}
fake rs(fake k){return k<<1|1;}
在线段树里,实际空间只能用到2*Maxn,但是为了避免越界,一般来说开到4*Maxn
这里用位运算会快的。ls表示左节点,rs表示右节点
维护
即push_up_sum()就是一个把儿子的信息传递给父亲
void push_up_sum(fake k){tree[k].sum=tree[ls(k)].sum+tree[rs(k)].sum;}
建树
即build函数
那么对于建树,由于二叉树自身的父子节点之间的可传递关系,所以可以考虑递归建树,并且在建树的同时,我们应该维护父子节点的关系:
void build(fake k,fake l,fake r){
tree[k].l=l,tree[k].r=r;tree[k].mul_lazy=1;
if(l==r){tree[k].sum=a[l];return ;}
fake mid=(l+r)>>1;
build(ls(k),l,mid);
build(rs(k),mid+1,r);
push_up_sum(k);
}
区间修改
即chS_add和chS_mul
先来谈谈懒惰标记
首先,懒标记的作用是记录每次、每个节点要更新的值,但线段树的优点不在于全记录(全记录依然很慢qwq),而在于传递式记录:
可以自己画图意会
void f(fake k,fake add,fake mul){
tree[k].sum=((tree[k].sum*mul)+(tree[k].r-tree[k].l+1)*add);
tree[k].mul_lazy=tree[k].mul_lazy*mul;
tree[k].add_lazy=(tree[k].add_lazy*mul+add);//更新一个点的懒惰标记
}
void push_down(fake k){
f(ls(k),tree[k].add_lazy,tree[k].mul_lazy);
f(rs(k),tree[k].add_lazy,tree[k].mul_lazy);
tree[k].add_lazy=0;tree[k].mul_lazy=1;
}
就是说你把一个节点的懒惰标记传递给儿子的操作,自己想想操作顺序
注意 懒惰标记是什么和本身无关,只关于传给节点的信息
于是按照push_up_sum的逆向思想,可以查找此区间
void chS_add(fake k,fake l,fake r,fake x){
if(l<=tree[k].l&&tree[k].r<=r){
tree[k].add_lazy=(tree[k].add_lazy+x);
tree[k].sum=(tree[k].sum+(tree[k].r-tree[k].l+1)*x);
return ;
}
push_down(k);
fake mid=(tree[k].l+tree[k].r)>>1;
if(l<=mid) chS_add(ls(k),l,r,x);
if(r>mid) chS_add(rs(k),l,r,x);
push_up_sum(k);
return ;
}
void chS_mul(fake k,fake l,fake r,fake x){
if(l<=tree[k].l&&tree[k].r<=r){
tree[k].mul_lazy=(tree[k].mul_lazy*x);
tree[k].add_lazy=(tree[k].add_lazy*x);
tree[k].sum=(tree[k].sum*x);
return ;
}
push_down(k);
fake mid=(tree[k].l+tree[k].r)>>1;
if(l<=mid) chS_mul(ls(k),l,r,x);
if(r>mid) chS_mul(rs(k),l,r,x);
push_up_sum(k);
return ;
}
区间查询
我们也用chS的思想,可以简单解决
fake query(fake k,fake l,fake r){
if(l<=tree[k].l&&tree[k].r<=r){return tree[k].sum;}
fake mid=(tree[k].l+tree[k].r)>>1;;
fake ret=0;
push_down(k);
if(l<=mid) ret+=query(ls(k),l,r);
if(r>mid) ret+=query(rs(k),l,r);
return ret;
}
AC代码(线段树2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <fstream>
#include <stdio.h>
#include <math.h>
#include <stack>
#include <map>
#include <queue>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
#define fake ll
#define size 666
using namespace std;
fake read(){
fake x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch<='9'&&ch>='0'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x;
}
const int Maxn=1e5+11;
struct TreeNode{
fake mul_lazy,add_lazy,sum,l,r;
}tree[Maxn*4];
fake a[Maxn],n,m,p,b,x,y;
fake ls(fake k){return k<<1;}
fake rs(fake k){return k<<1|1;}
void build(fake k,fake l,fake r);
void push_up_sum(fake k);//献上和
void push_down(fake k);//向下推进懒惰标记
void f(fake k,fake add,fake mul);
void chS_mul(fake k,fake l,fake r,fake x);
void chS_add(fake k,fake l,fake r,fake x);
fake query(fake k,fake l,fake r);
void f(fake k,fake add,fake mul){
tree[k].sum=((tree[k].sum*mul)+(tree[k].r-tree[k].l+1)*add)%p;
tree[k].mul_lazy=tree[k].mul_lazy*mul%p;
tree[k].add_lazy=(tree[k].add_lazy*mul+add)%p;//更新一个点的懒惰标记
}
void push_up_sum(fake k){tree[k].sum=(tree[ls(k)].sum+tree[rs(k)].sum)%p;}
void build(fake k,fake l,fake r){
tree[k].l=l,tree[k].r=r;tree[k].mul_lazy=1;
if(l==r){tree[k].sum=a[l]%p;return ;}
fake mid=(l+r)>>1;
build(ls(k),l,mid);
build(rs(k),mid+1,r);
push_up_sum(k);
}
void push_down(fake k){
f(ls(k),tree[k].add_lazy,tree[k].mul_lazy);
f(rs(k),tree[k].add_lazy,tree[k].mul_lazy);
tree[k].add_lazy=0;tree[k].mul_lazy=1;
}
void chS_add(fake k,fake l,fake r,fake x){
if(l<=tree[k].l&&tree[k].r<=r){
tree[k].add_lazy=(tree[k].add_lazy+x)%p;
tree[k].sum=(tree[k].sum+(tree[k].r-tree[k].l+1)*x)%p;
return ;
}
push_down(k);
fake mid=(tree[k].l+tree[k].r)>>1;
if(l<=mid) chS_add(ls(k),l,r,x);
if(r>mid) chS_add(rs(k),l,r,x);
push_up_sum(k);
return ;
}
void chS_mul(fake k,fake l,fake r,fake x){
if(l<=tree[k].l&&tree[k].r<=r){
tree[k].mul_lazy=(tree[k].mul_lazy*x)%p;
tree[k].add_lazy=(tree[k].add_lazy*x)%p;
tree[k].sum=(tree[k].sum*x)%p;
return ;
}
push_down(k);
fake mid=(tree[k].l+tree[k].r)>>1;
if(l<=mid) chS_mul(ls(k),l,r,x);
if(r>mid) chS_mul(rs(k),l,r,x);
push_up_sum(k);
return ;
}
fake query(fake k,fake l,fake r){
if(l<=tree[k].l&&tree[k].r<=r){return tree[k].sum%p;}
fake mid=(tree[k].l+tree[k].r)>>1;;
fake ret=0;
push_down(k);
if(l<=mid) ret+=query(ls(k),l,r);
if(r>mid) ret+=query(rs(k),l,r);
return ret%p;
}
int main(){
freopen("Interval Tree.in","r",stdin);
n=read();m=read();p=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++){
b=read();x=read();y=read();
if(b==1) chS_mul(1,x,y,read());
else if(b==2) chS_add(1,x,y,read());
else if(b==3) printf("%lld
",query(1,x,y));
}
return 0;
}
嵬
愿线段树陪你出走半生,归来仍是少年。