【算法#6】线段树
如果我没记错的话应该是#6了。
今天稍微搞懂了基础的线段树,但是因为其他的数据结构和算法都没有完全理解(无法处理穿衣服的题)所以说是咕掉了而不是get。
首先搞一下线段树的结构。
本质上就是一个按层次编号的二叉树,总共需要的空间大概在\(2n\)左右,注意别开小了。
1~8 | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1~4 | 5~8 | |||||||||||||
1~2 | 3~4 | 5~6 | 7~8 | |||||||||||
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
大概就是这个样子,第一层有\(2^0\)个节点,第i层有\(2^{i-1}\)个节点。
每个节点携带相对于该段的附加信息,(一般来说对于不同规模的数据,附加信息的规模应当相同否则就没有实用数据结构的意义了。)
对于每个查询的区间,如果包含子右区间,那么递归进入子右区间,如果包含子左区间,递归进入子左区间并且改变查询区间的规模直到当前节点的范围完全契合查询区间。也就是,我们把原查询区间变成不超过log区间长度个(是这样吗?)的线段的并。很好理解对吧?我们上板子。
int ql,qr;
<modify> query(int o,int l,int r){
int m=l+(r-l)/2,ans=<modify>;
if(ql<=l&&r<=qr)
return <res>[o];
if(ql<=m)
ans=<modify>(ans,query(o*2,l,m));
if(qr>m)
ans=<modify>(ans,query(o*2+1,m+1,r));
return ans;
}
这个码风好诡异啊……这里是查询的,修改一下我把新码风的发来。
int lb[maxn*2],rb[maxn*2];
<modify> query(int o,int l,int r){
int m=lb[o]+(rb[o]-lb[o])/2,ans=<modify>;
if(lb[o]==l&&r==rb[o])
return <res>[o];
if(l<=m)
ans=<modify>(ans,query(o*2,l,m));
if(r>m)
ans=<modify>(ans,query(o*2+1,m+1,r));
return ans;
}
理论上没有问题?因为l和r都是可以预先处理好的。
然后是update,单点修改。
int p,v;
void update(int o,int l,int r){
int m=l+(r-l)/2;
if(l==r)
<res>[o]=v;
else {
if(p<=m)
update(o*2,l,m);
else update(o*2+1,m+1,r);
<res>[o]=<modify>(<res>[o*2],<res>[o*2+1]);
}
}
然后是构造过程,当然,对于每个点进行一次update操作也是可行的,只是复杂度上是nlogn,要是预先设置好每个叶节点的值再递归构造能够达到线性n的复杂度。
int a[maxn];
void build(int o,int l,int r){
m=l+(r-l)/2;
if(l==r)
<res>[o]=a[o-maxn+1];
else {
build(o*2,l,m);
build(o*2+1,m+1,r);
<res>[o]=<modify>(<res>[o*2],<res>[o*2+1]);
}
}
大概就是这样,点修改,更新,查询。学了后面的我再来补充。