z k w zkw zkw线段树作为循环式线段树具有较小的常数.(其实树状数组本质上就是线段树…
初始化
下标为 [ 1 , n ] [1,n] [1,n],预处理一个 2 k > n 2^k>n 2k>n.
然后总空间为 2 k + 1 2^{k+1} 2k+1.( 2 k + 1 < 4 n 2^{k+1}<4n 2k+1<4n)
后面令 k = 2 k k=2^k k=2k.
那么一个叶子 x x x的位置在 x + k x+k x+k.
单点修改
for(x += k; x;x /= 2) ....
直接遍历到根
区间修改
因为
z
k
w
zkw
zkw线段树是自底往上求,所以一般使用标记永久化的
t
r
i
c
k
trick
trick.
所以如果维护的标记比较繁杂,还是打普通线段树比较好.
当遇到区间求和和区间修改的时候,我们令 l + = k − 1 , r + = k + 1 l+=k-1,r+=k+1 l+=k−1,r+=k+1,即变为开区间.(这个是为了和的上传
当只有区间求和和单点修改的时候,我们可以考虑转化为左开右闭区间.
一个区间加是这样的:
void add(int l,int r,ll x) {
int L=0,R=0,len=1;
for(l += k-1, r += k+1;l^r^1;l/=2,r/=2,len*=2) {//l^r^1表示l,r不为兄弟节点
if(~l & 1) ad[l^1] += x, s[l^1] += len*x, L += len;
if( r & 1) ad[r^1] += x, s[r^1] += len*x, R += len;
s[l>>1] += L*x;
s[r>>1] += R*x;
}
for(L += R;l/=2; s[l] += L*x);
}
对应的区间求和:
ll sum(int l,int r) {
ll ans=0;
int L=0,R=0,len=1;
for(l += k-1, r += k+1;l^r^1;l/=2,r/=2,len*=2) {
if(~l & 1) ans += s[l^1], L += len;
if( r & 1) ans += s[r^1], R += len;
ans += ad[l>>1]*L+ad[r>>1]*R;
}
for(L+=R;l/=2;ans+=ad[l]*L);
return ans;
}
区间 min , max \min,\max min,max
这个在没有修改的情况下还是很好求的,只要预处理一下就好.
但是如果遇上区间标记呢?(比如区间加
z k w zkw zkw在 P P T PPT PPT内有讲到一个核心思想:
标记和值都是相对的数,那么我们何必同时维护标记和值呢?
为了同化,我们直接差分,这样取原值我们直接遍历到根求和即可.
具体地,比方说我们要维护区间 min \min min:
那么我们令 d m n [ i ] = m n [ i ] − m n [ i / 2 ] dmn[i]=mn[i]-mn[i/2] dmn[i]=mn[i]−mn[i/2], m n [ i ] mn[i] mn[i]表示区间最小值,代码内实际上只保留 d m n dmn dmn.
预处理:
for(int i=1;i<=n;i++) mn[i+k]=a[i];
for(int i=k-1; i;i--) {
int A=min(mn[i*2],mn[i*2|1]);
mn[i] += A; mn[i*2] -= A; mn[i*2+1] -= A;
}
对应的区间加应该是这样的:
void upd(int x) {
int A=min(mn[x*2],mn[x*2|1]);
mn[x] += A; mn[x*2] -= A; mn[x*2+1] -= A;
}
void add(int l,int r,ll x) {
for(l += k-1,r += k+1;l^r^1;l/=2,r/=2) {
if(~l & 1) mn[l^1] += x;
if( r & 1) mn[r^1] += x;
upd(l/2); upd(r/2);
}
for( ;l/=2;upd(l));
}
求 min \min min:
int Min(int l,int r) {
if(l == r) return ask(l);//特判,否则会死循环
int L=0,R=0;
for(l += k, r += k; l^r^1;l=l/2,r=r/2) {
L += mn[l]; R += mn[r];
if(~l&1) cmin(L,mn[l^1]);
if( r&1) cmin(R,mn[r^1]);
}
int ans=min(L+mn[l],R+mn[r]);
for( ;l /= 2; ) ans += mn[l];
return ans;
}