线段树用来降复杂度,到O(nlogn),往往涉及的关键词:区间,实时更新。
#define ls i<<1
#define rs i<<1|1
#define mid (l+r>>1)
#define lson ls,l,mid
#define rson rs,mid+1,r
#define ln (mid-l+1)
#define rn (r-mid)
//上面是常用的,并且ln,rn得加括号
#define ison i,l,r //吐槽:这个是我自创的,有点歧义,还是一般用i,l,r写吧
int m=l+r>>1
数组开4倍空间 <<2
有些写法中,pushup和pushdown函数都只有两个变量
void pushup(int i,int m) --->pushup(i,r-l+1)
void pushdown(int i,int m) --->pushdown(i,r-l+1)
这时
(m-(m>>1))==m-l+1==ln
(m>>1)==r-m==rn
区间覆盖
区间覆盖本质还是区间修改,修改成同一个值,只不过区间覆盖下,普通的离散化会有缺陷。上面博客中有样例。
这个点本身不难,但这种点很好的体现了延迟标记的作用,而这正是线段树节省时间的关键点之一,涉及到这个区间时才用延迟标记下推下去。要考虑pushdown函数(下推)什么时候可以不用加,所以所有操作下来,不是所有的下推函数推到底的。
单单区间覆盖不用上推,但不意味着涉及区间覆盖不用上推pushup函数、
区间合并
数组内的各个值往往含有某种关系式,最后问一个区间或这一点包含的区间的关系式的值,同时含有单点或区间修改操作。
代码思路:变量要设三个,其他操作类似
int lc[maxn<<2],rc[maxn<<2],num[maxn<<2];
lc (最)左区间长度
rc (最)右区间长度
num 当前区间最大长度
rc[ls]+lc[rs] 中间长度(前提是符合 合并条件)
区间合并+单点修改操作环境下
//左区间,右区间,当前区间的关系式值
int lc[maxn<<2],rc[maxn<<2],num[maxn<<2];
//上推操作视题意而定
void pushup(int i,int l,int r){
//正常情况下的区间上推
lc[i]=lc[ls]; lc[i]=rc[rs];
num[i]=max(num[ls],num[rs]);
//当中间符合 合并条件时
int m=l+r>>1;
//下面再次更新lc[i]、lc[i]、num[i]
......
}
//建树操作一般相同
void build(int i,int l,int r){
if(l==r){
lc[i]=rc[i]=num[i]=1;
return;
}
int m=l+r>>1;
build(lson); build(rson);
pushup(i,l,r);
}
//单点修改a[k]=x时
void update(int i,int l,int r,int k,int x){
if(l==r&&l==k){
.....
return;
}
int m=l+r>>1;
if(k<=m) update(lson,k,x);
else update(rson,k,x);
pushup(i,l,r);
}
//区间查询
int query(int i,int l,int r,int x,int y){
if(x<=l&&r<=y) return num[i];
int m=l+r>>1;
int temp=0;
....
return temp;
}
区间合并+区间修改操作环境下
void pushup(int i,int l,int r){
//正常情况下的区间上推
lc[i]=lc[ls]; lc[i]=rc[rs];
num[i]=max(num[ls],num[rs]);
//当中间符合 合并条件时
int m=l+r>>1;
//下面再次更新lc[i]、lc[i]、num[i]
......
}
void pushdown(int i,int l,int r){
if(f[i]!=-1){
.....
f[i]=-1; //标记重置
}
}
void build(int i,int l,int r){
//lc[i],rc[i],num[i]修改
....
f[i]=-1;
if(l==r) return;
int m=l+r>>1;
build(lson); build(rson);
}
void update(int i,int l,int r,int x,int y,int c){
if(x<=l&&r<=y){
//lc[i],rc[i],num[i]修改
....
f[i]=c;
return;
}
pushdown(i,l,r);
int m=l+r>>1;
if(x<=m) update(lson,x,y,c);
if(y>m) update(rson,x,y,c);
pushup(i,l,r);
}
int query(int i,int l,int r,int x){
int m=l+r>>1;
pushdown(i,l,r);
//下面视题意而定
....
}
母题:HDU 1540(单点修改环境下)
练习题:
1、HDU3308 LCIS(LCIS+单点修改环境下)
3、
扫描线
待完善
可持久化线段树
入门:求静态第k小
源于线段树,还加了前缀和的思想,并且用公共的节点优化。一般静态开20倍,也可以<<5.
设原序列有n个数,含有m次询问
空间复杂度:(建空树)4*n+(前缀和更新)nlog2n
一般我们数组大小就开nlog2n (动态不一样)
时间复杂度:mlog2n
其他
HDU-4614 线段树+二分
2020CCPC威海 Caesar Cipher线段树+哈希