膜拜ZKW神犇。。。。Orz。。。
ZKW线段树的思想是直接找到一个大区间对应的小区间
由于是自底向上的,所以常数很小(《统计的力量》)
其实我觉得,真正难写 的是区间修改
比如现在造一颗线段树,支持区间求和,区间修改
ZKW线段树有两种做法:
一是维护神奇 的前缀和的前缀和
二是沿用递归版线段树的思路,在 区间上打标记,然后统计
我 太弱,第一种不会。。。于是我就用第二种吧
区间修改:简单,就在区间上打个标记,顺便更新一下遍历到的区间和,最后当l和r父亲相同时,循环结束,再把他们的父亲们的和更新。
区间求和:先是记一下左边,右边 的区间里有几个元素要加,每次就根据遍历到的区间的标记更新答案。最后当l和r父亲相同时,循环结束,再把他们的父亲们的标记拿来更新答案
好像有点大话连篇。。。上代码好了
const int maxn=262144;
int sumv[maxn],addv[maxn],m;
//sumv 维护区间的和,addv是标记;m不说了吧,地球人都知道
void update(int l,int r,int a){
l+=m-1,r+=m+1;
int ln=0,rn=0;//记录l,r遍历到了几个元素
int s=1;//记录这一层的区间有几个元素
for(;l^r^1;l>>=1,r>>=1,s<<=1){
if(ln)sumv[l]+=ln*a;
if(rn)sumv[r]+=rn*a;
if(~l&1)addv[l^1]+=a,ln+=s;//只更新标记啊
if(r&1)addv[r^1]+=a,rn+=s;
}
data[l]+=ln*a;data[r]+=rn*a;
for(ln+=rn,l>>=1;l;l>>=1)
data[l]+=ln*a;//我的父亲们~
}
int query(int l,int r){
l+=m-1,r+=m+1;
int sum=0;
int ln=0,rn=0;
for(int s=1;l^r^1;l>>=1,r>>=1,s<<=1){
if(ln)sum+=addv[l]*ln;//根据标记更新和
if(rn)sum+=addv[r]*rn;
if(~l&1)sum+=data[l^1]+addv[l^1]*s,ln+=s;
if(r&1)sum+=data[r^1]+addv[r^1]*s,rn+=s;
}
sum+=addv[l]*ln+addv[r]*rn;
for(ln+=rn,l>>=1;l;l>>=1)
sum+=addv[l]*ln;//我的父亲们~
return sum;
}
//PS:有错误就说一说啊。。。
现在,某人又要求造一颗线段树支持 区间修改,区间最小值
还是沿用区间求和的思路,只是代码有点不同
对于更新最小值的操作,不能单纯的加减了,而是要根据儿子的值而修改。
const int maxn=262144;
int data[maxn],addv[maxn],m,n;
//data是区间最小
int query(int l,int r){
int lmax=INT_MAX,rmax=INT_MAX;
int s=l+m-1,t=r+m+1;
bool _l=false,_r=false;
//叶节点不能通过儿子更新,判断是否可以更新
//其实好像下面有更好的判断方式。。。懒得改了
for(;s^t^1;s>>=1,t>>=1){
if(_l)lmax+=addv[s];
if(_r)rmax+=addv[t];
if(~s&1)lmax=min(lmax,data[s^1]+addv[s^1]),_l=true;
if(t&1)rmax=min(rmax,data[t^1]+addv[t^1]),_r=true;
}
lmax+=addv[s];rmax+=addv[t];
lmax=min(lmax,rmax);
for(s>>=1;s;s>>=1)lmax+=addv[s];
return lmax;
}
void add(int l,int r,int a){
int s=l+m-1,t=r+m+1;
for(;s^t^1;s>>=1,t>>=1){
if(s<l+m-1)data[s]=min(data[s<<1]+addv[s<<1],data[s<<1|1]+addv[s<<1|1]);
if(t<r+m+1)data[t]=min(data[t<<1]+addv[t<<1],data[t<<1|1]+addv[t<<1|1]);
if(~s&1)addv[s^1]+=a;
if(t&1)addv[t^1]+=a;
}
data[t]=min(data[t<<1]+addv[t<<1],data[t<<1|1]+addv[t<<1|1]);
for(;s;s>>=1)
data[s]=min(data[s<<1]+addv[s<<1],data[s<<1|1]+addv[s<<1|1]);
}
嘿嘿嘿。。。
我的线段树~