打了这么久比赛连线段树都不会就离谱
参考资料 :
线段树详解 (原理,实现与应用), CSDN,岩之痕
《统计的力量》,清华大学,张昆玮
今夜,我是ikun.
简介
zkw线段树是一种非递归的线段树,以常数小、代码短著称。
单点加减,区间求和
//zkw线段树,只能查询[1,m-2]的范围
struct SGT
{
ll save[M<<2], m;
void build(int n) //含read
{
for(m=1; m<=n+1; m<<=1) continue;
for(int i=m+1; i<=m+n; ++i) save[i] = read();
for(int i=m-1; i>=1; --i)
save[i] = save[i<<1] + save[i<<1|1];
}
ll query(int s, int t) //sum[s,t]
{
ll ans = 0;
for(s=s+m-1, t=t+m+1; s^t^1; s>>=1, t>>=1)
{
if(~s&1) ans+=save[s^1];
if( t&1) ans+=save[t^1];
}
return ans;
}
void modify(int n, ll val) //save[n]+=val
{
for(save[n+=m]+=val, n>>=1; n; n>>=1)
save[n] = save[n<<1] + save[n<<1|1];
}
}sgt;
存储性质:
- zkw线段树的存储方式是堆式存储,叶子节点数有 个,前面的节点数有 个,总结点数 个, 是大于 的最小的2的幂。所以需要开4倍空间。
- 根节点的编号是 ,非叶子节点的编号是 ,叶子节点的编号是 。原数组 在线段树中对应 .
- 叶子节点存储数组本身,每个非叶子节点存储它两个儿子的和。建树复杂度
单点修改:
修改位置加 可以直接获得叶子下标,首先将叶子里存储的值修改,然后向上递归即可,复杂度 .
区间求和
- 注意,zkw线段树是将闭区间转化为开区间做查询,实际可查询的最大闭区间是 ,超过这个范围就需要平移或者扩容。
- 首先获得开区间对应的叶子下标 ,然后不断向上迭代直至两个下标成为兄弟节点(异或值为一)时停止。
- 迭代过程中,如果 的最末位为 ,那么 异或 这个节点对应的值需要被加上。
- 同理,如果 的最末位为 ,那么 异或 这个节点对应的值也需要被加上。
- 复杂度
举例:求闭区间 的和(图片来源于《统计的力量》)
- 首先转化为开区间 ,对应叶子下标
- ,那么 ,向上迭代
- ,那么 ,(5对应的值需要被算,7不用),向上迭代
- ,两者互为兄弟,退出。
bouns:计算前缀和时用不到所有的右孩子节点,即【(左)线段树=树状数组】。
扫描二维码关注公众号,回复:
9221388 查看本文章
例题:HDU1754
单点修改,区间求max.
把上述的所有和操作改成max即可。
/* LittleFaint : Heinto! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 200016, MOD = 1000000007;
//zkw线段树,只能查询[1,m-2]的范围
struct SGT
{
int save[M<<2], m;
void build(int n) //含read
{
for(m=1; m<=n+1; m<<=1) continue;
for(int i=m+1; i<=m+n; ++i) save[i] = read();
for(int i=m-1; i>=1; --i)
save[i] = max(save[i<<1], save[i<<1|1]);
}
int query(int s, int t) //sum[s,t]
{
int ans = 0;
for(s+=m-1, t+=m+1; s^t^1; s>>=1, t>>=1)
{
if(~s&1) ans=max(ans, save[s^1]);
if( t&1) ans=max(ans, save[t^1]);
}
return ans;
}
void modify(int n, int val) //save[n]=val
{
for(save[n+=m]=val, n>>=1; n; n>>=1)
save[n] = max(save[n<<1], save[n<<1|1]);
}
}sgt;
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
sgt.build(n);
while(m--)
{
char op[3]; int x, y;
scanf("%s%d%d", op, &x, &y);
if(op[0]=='U')
sgt.modify(x, y);
else
printf("%d\n",sgt.query(x,y) );
}
}
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}