ZKW线段树
杂说
最近老是被数据结构血虐,目前就学了 字典树,树状数组,准备学线段树,看到网上一直在推广zkw线段树。所以决定学这个。
zkw线段树优点有很多,常数小,好写,等等。不再叙述(因为我也不懂)
总的来说,就我个人理解,zkw线段树总共分两种,一种就是普通维护:需要什么维护什么,比如维护区间和,维护区间最大值、最小值等等。另一种就是差分维护:节点存储与父节点(一开始父节点为两个子节点的最小值)的差值。
普通维护
首先是建树
建树
建树自然是要先预分配空间,我的分配方法是先算出位数,比如他有1-n的区间需要维护,那么先求出 int(logn2) i n t ( log 2 n ) +2,然后
const int N=1<<k;//k就是上面的值,先预先求好
int d[N];
int M;
空间分配好之后就建树
void build(int n){
for(M=1;M<n;M<<=1);
//M是叶子节点开始储存的位置
for(int i=M+1;i<=M+n;i++)
scanf("%d",&d[i]);
//以上在建立叶子节点
for(int i=M-1;i;i--)
//d[i]=min(d[i<<1],d[i<<1|1]);
//这样子算是维护最小值
d[i]=d[i<<1]+d[i<<1|1];//维护和
//以上在建立父节点
}
建树代码很简单就完成
维护
单点修改
x是元素位置,v是元素增量
void Change(int x,int v){
d[x=M+x]+=v;
while(x) d[x>>=1]=d[x<<1]+d[x<<1|1];
}
区间修改
还不会。
查询
区间和查询
int Sum(int s,int t,int Ans=0){
for (s=s+M-1,t=t+M+1;s^t^1;s>>=1,t>>=1){//先变开区间
if(~s&1) Ans+=d[s^1];//&1判断是不是右子树,^1另一个子树
if( t&1) Ans+=d[t^1];
}
return Ans;
}
区间和查询
int Min(int s,int t,int ans=999999){
for (s=s+M-1,t=t+M+1;s^t^1;s>>=1,t>>=1){//先变开区间
if(~s&1) ans=min(ans,d[s^1]);//&1判断是不是右子树,^1另一个子树
if( t&1) ans=min(ans,d[t^1]);
}
return ans;
}