线段树(四)——杨子曰算法
传送门:线段树(三)
欧,现在是线段树模板题的最后一个大BOSS了,也就是更新的是区间,询问的也是区间,看上去很恶心,现在解决它!(我们把add(lazy)和sum记在一个struct,看得清晰)
首先,他询问的是区间和,So,最开始肯定要build,不多说:
void build(int l,int r,int nod){
tree[nod].add = 0;
if (l==r){
tree[nod].sum=a[l];
return;
}
int mid=(l+r)/2;
build(l,mid,nod*2);
build(mid+1,r,nod*2+1);
tree[nod].sum=tree[nod*2].sum+tree[nod*2+1].sum;
}
再看update,这肯定得用lazy优化,就是区间吻合就完事,但更新sum的时候一定要注意区间吻合时,这个区间里的每一个元素可都得加上v,So,tree[nod].sum+=(r-l+1)*v
来看update:
void update(int l,int r,int ll,int rr,int v,int nod){
if (l==ll &&r==rr){
tree[nod].sum+=(r-l+1)*v;
tree[nod].add+=v;
return;
}
pushdown(nod, l, r);
int mid=(l+r)/2;
if (rr<=mid) update(l,mid,ll,rr,v,nod*2);
else if (ll>=mid+1) update(mid+1,r,ll,rr,v,nod*2+1);
else {
update(l,mid,ll,mid,v,nod*2);
update(mid+1,r,mid+1,rr,v,nod*2+1);
}
pushup(nod);
}
这里有一个小问题pushdown怎么写,机智的大佬一定会写了,但我还要说一下,这个add是指对这个区间中的每个数都要加上它,那在计算左右儿子的sum的时候,要乘上儿子区间的对应元素个数(这也就是为什么pushdown要传l和r)
void pushdown(int nod, int l, int r) {
int mid = (l+r)/2;
tree[nod*2].sum+=(mid-l+1)*tree[nod].add;
tree[nod*2+1].sum+=(r-mid)*tree[nod].add;
tree[nod*2].add+=tree[nod].add;
tree[nod*2+1].add+=tree[nod].add;
tree[nod].add=0;
}
还有一个小问题,pushup怎么写?sum一定要pushup上去,那add呢?Of couse NOT!首先add本身记录的就是它儿子的变化量,再有,当你pushup的时候,你的add可是0诶,那你pushup什么鬼呢?
void pushup(int nod) {
tree[nod].sum = tree[nod*2].sum + tree[nod*2+1].sum;
}
这样弄完了以后solve就简单了,更原来完全一样:
int solve(int l,int r,int ll,int rr,int nod){
if (l==ll &&r==rr){
return tree[nod].sum;
}
pushdown(nod, l, r);
int mid=(l+r)/2;
if (rr<=mid) return solve(l,mid,ll,rr,nod*2);
else if(ll>=mid+1) return solve(mid+1,r,ll,rr,nod*2+1);
else return solve(l,mid,ll,mid,nod*2)+solve(mid+1,r,mid+1,rr,nod*2+1);
}
OK!完事
啰嗦一下:
如果到目前为止,你对线段树一点问题都没有的话,Congratulations!——你终于略懂了线段树的皮毛,当然线段树的用处肯定不只是求求min,求求max,求求sum,之前也曰了线段树可以解决所有区间问题,对应的,线段树里记录的区间信息是不一样的
看道题:
给一个长度为n(n<=200000)的序列,再给出m个操作,对于每个操作,先给出一个k,如果k=1,则输入x,y,输出区间[x,y]中能被7整除的数的个数,如果k=2,则输入x,y,z,把区间[x,y]的每个元素加上z
好吧,最近写线段树写烦了,我也不知道什么时候再出线段树(五),等那时候再来给大家曰这道题吧!
OK,88
未经作者允许,严禁转载: