(上篇好像是好久以前的事了/捂脸)
嗯。。下篇。。主要谈线段树的优化。。
1.位运算(想必大家都会)
在向下递归时 p*2改成p<<1
p*2+1改成p<<1|1
解释:
p<<=i相当于p*2^i
p<<1 后 是偶数,在二进制下 末尾为0 |1 后 相当于 +1
(这都不是重点,下面重点开始)
2.延迟标记
由例题引入:
给定一个包含n个数的序列,初值全为0,现对这个序列有两种操作:
操作1:将第k1 个数 到 第k2 个数加1;
操作2:查询 从第k1个数到第k2个数得最大值。(k1<=k2<=n)
所有的数都 <=100000
题面很简洁。。但是实现就不一定了。我们所知线段树支持单点修改,但本题要求区间修改,怎么办。最初始的想法自然是把要修改的区间内每一个数都单点修改一次。。但复杂度就会变成O(n^2),这是我们不能承受的。思考:如果一个区间修改后我们之后都没有去查询,那么这次修改就是无用的,我们就从这里着手优化。
我们给线段树上每个点一个标记,表示这个点已被修改,但其儿子还未修改,这个就叫延迟标记。这样就可以保证复杂度是O(nlogn),当然,常数更大了。。(线段树的常数反正是没救了)证明略
贴代码:(我才不会说这段代码都没编译过)
void change(int l,int r,int k)
{
if(y<l||x>r) return;
if(x<=l&&y>=r)
{
tree[k].maxx++;
tree[k].lazy++;
return;
}
int tz=tree[k].lazy,mid=(l+r)/2;
tree[k*2].maxx+=tz;
tree[k*2+1].maxx+=tz;
tree[k*2].lazy+=tz;
tree[k*2+1].lazy+=tz;
tree[k].lazy=0;
change(l,mid,k*2);
change(mid+1,r,k*2+1);
tree[k].maxx=max(tree[k*2].maxx,tree[k*2+1].maxx);
return;
}
int ask(int l,int r,int k)
{
if(y<l||x>r) return -1;
if(x<=l&&y>=r)
return tree[k].maxx;
int tz=tree[k].lazy,mid=(l+r)/2;
tree[k*2].lazy+=tz;
tree[k*2+1].lazy+=tz;
tree[k*2].maxx+=tz;
tree[k*2+1].maxx+=tz;
tree[k].lazy=0;
return max(ask(l,mid,k*2),ask(mid+1,r,k*2+1));
}
注:建树过程没区别,标记的要如何灵活运用还是要自己靠刷题感觉
-------------------------------------------------------------------------坑总算是填完了---------------------------------------------------------------------------