线段树
1.线段树是什么
线段树顾名思义,它是一颗树,而且是一颗完全二叉树,它的每一个位置用来保存一条线段,故称为线段树。
2.线段树的作用
维护区间极值或和或乘积等
3.模板
先定义
#define ls x<<1 左儿子
#define rs ls|1 右儿子
#define LL ls,l,M
#define RR rs,M+1,r
3.1建树
if(l==r){f[x]=a[l];return;}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
update(x);//f[x]=max(f[x],f[ls],f[rs]);
//f[x]=f[ls]+f[rs];
3.2单点修改
if(l==r){
f[x]=p;
return;
}
int mid=(l+r)>>1;
if(k<=mid) find(ls,l,mid,l2,r2);
else find(rs,mid+1,r,l2,r2);
3.3查询
if(l==l2&&r==r2){
ans+=f[x];//ans=max(ans,f[x]);
return;
}
int mid=(l+r)>>1;
if(r2<=mid) find(ls,l,mid,l2,r2);
else if(l2>mid) find(rs,mid+1,r,l2,r2);
else{find(ls,l,mid,l2,mid);find(rs,mid+1,r,mid+1,r2);}
3.4区间修改
if(l==l2&&r==r2){
bz[x]=1;
修改f[x]的信息
f[x]+=z/f[x]*=z...
}
int down(int x){
if(bz[x]){
修改f[ls]以及f[rs]的信息
bz[x]=0;
bz[ls]=bz[rs]=1;
/或bz[ls]+=bz[x],bz[rs]+=bz[x],bz[x]=0;
/或bz[ls]*=bz[x],bz[rs]*=bz[x],bz[x]=0;
}
}
3.5查找前驱
查找最前的一个满足f[x]>=val的位置
找到就退出
I sek(I l2,I r2,I val,I x=1,I l=1,I r=n){
if(f[x].max<val||l>r2||r<l2) return 0;
if(l==r) return l;
I M=l+r>>1,t;
return (t=sek(l2,r2,val,LL))?t:sek(l2,r2,val,opt,RR);
}
3.6查找后继
I sek(I l2,I r2,I val,I x=1,I l=1,I r=n){
if(f[x].max<val||l>r2||r<l2) return 0;
if(l==r) return l;
I M=l+r>>1,t;
return (t=sek(l2,r2,val,opt,RR))?t:sek(l2,r2,val,LL);
}
其实差别不大,打到一起就是这样。
I sek(I l2,I r2,I val,I op,I x=1,I l=1,I r=n){//op表示查找前驱还是后继
if(f[x].max<val||l>r2||r<l2) return 0;
if(l==r) return l;
I M=l+r>>1,t;
return (!op)?(t=sek(l2,r2,v,op,LL))?t:sek(l2,r2,v,op,RR)
:(t=sek(l2,r2,v,op,RR))?t:sek(l2,r2,v,op,LL);
}
4.动态开点
将dg中用ls[]和rs[]来代替ls或rs
在建树或修改中判断
if(!ls[x]) ls[x]=++cnt;
dg(ls[x],l,mid);
if(!rs[x]) rs[x]=++cnt;
dg(rs[x],mid+1,r);
5.线段树合并
对于不同的题目线段树合并有不同的打法。
假设我们要将线段树Y合并到线段树X上。
1.可直接用线段树Y的每个节点更新线段树X上的节点(一般适用于区间极值)
int marge(int x,int y){
if(!x||!y) return x+y;
ls[x]=marge(ls[x],ls[y]);
rs[x]=marge(rs[x],rs[y]);
f[x]=max(f[x],f[y]);
}
2.用线段树Y的叶子更新线段树X的叶子,然后再用线段树X的左右儿子更新线段树X(一般用于区间和)
int marge(int x,int y,int l,int r){
if(!x||!y) return x+y;
if(l==r){
f[x]+=f[y];
return x;
}
int M=(l+r)>>1;
ls[x]=marge(ls[x],ls[y],l,M);
rs[x]=mge(rs[x],rs[y],M+1,r);
update(x);
return x;
}
练习(例题):
1959. 最大值
1960. 最大值2
3512. 【NOIP2013模拟11.5A组】游戏节目
5961. 【NOIP2018提高组D1T1】铺设道路
4231. 【五校联考4day1】寻找神格
作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/