【笔记】ZKW线段树一:单点修改,区间查询

打了这么久比赛连线段树都不会就离谱

参考资料 :
线段树详解 (原理,实现与应用), CSDN,岩之痕
《统计的力量》,清华大学,张昆玮

今夜,我是ikun.

简介

zkw线段树是一种非递归的线段树,以常数小、代码短著称。

单点加减,区间求和

洛谷P3374 【模板】树状数组 1

//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;

存储性质:

  1. zkw线段树的存储方式是堆式存储,叶子节点数有 m m 个,前面的节点数有 m 1 m-1 个,总结点数 2 m 1 2*m-1 个, m m 是大于 n + 1 n+1 的最小的2的幂。所以需要开4倍空间
  2. 根节点的编号是 1 1 ,非叶子节点的编号是 [ 1 , m ) [1,m) ,叶子节点的编号是 [ m , 2 m ) [m,2m) 。原数组 [ 1 , n ] [1,n] 在线段树中对应 [ m + 1 , m + n ] [m+1,m+n] .
  3. 叶子节点存储数组本身,每个非叶子节点存储它两个儿子的和。建树复杂度 O ( n ) O(n)

单点修改:

修改位置加 m m 可以直接获得叶子下标,首先将叶子里存储的值修改,然后向上递归即可,复杂度 O ( l o g n ) O(logn) .

区间求和

  1. 注意,zkw线段树是将闭区间转化为开区间做查询,实际可查询的最大闭区间是 [ 1 , m 2 ] [1,m-2] ,超过这个范围就需要平移或者扩容。
  2. 首先获得开区间对应的叶子下标 ( s , t ) (s,t) ,然后不断向上迭代直至两个下标成为兄弟节点(异或值为一)时停止。
  3. 迭代过程中,如果 s s 的最末位为 0 0 ,那么 s s 异或 1 1 这个节点对应的值需要被加上。
  4. 同理,如果 t t 的最末位为 0 0 ,那么 t t 异或 1 1 这个节点对应的值也需要被加上。
  5. 复杂度 O ( l o g n ) O(logn)

举例:求闭区间 [ 1 , 4 ] [1,4] 的和(图片来源于《统计的力量》)

  1. 首先转化为开区间 ( 0 , 5 ) (0,5) ,对应叶子下标 s = 8 , t = 13 s=8,t=13
  2. s = 8 , t = 13 s=8,t=13 ,那么 a n s + = s a v e [ s 1 ] + s a v e [ t 1 ] ans+=save[s \oplus1 ]+save[t\oplus1] ,向上迭代
  3. s = 4 , t = 6 s=4,t=6 ,那么 a n s + = s a v e [ s 1 ] ans+=save[s\oplus 1] ,(5对应的值需要被算,7不用),向上迭代
  4. s = 2 , t = 3 s=2,t=3 ,两者互为兄弟,退出。

在这里插入图片描述
在这里插入图片描述
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;
}
发布了375 篇原创文章 · 获赞 305 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/102993829