PJ蒟蒻的zkw线段树详解(2)--单点修改_查询区间和

传送门

让我们先来一道特别简单的题目--单点修改与查询区间和

先贴一套PPT里面的图

 

上面就是zkw线段树的查询区间和方法...

当然,直接贴图当然很玄学...

分步来:

  1. 找到树的深度m[这里的深度定义为树的层数].ps:如果这个数不是满二叉树会自动将其添满0.

  2. 找出根到第一个叶子节点的距离,距离为2^{m}.

  3. 然后将每个数字读入,数字在线段树中的下标为i+m.

  4. 建树,对于每一棵子树,它的根节点是两个儿子的节点之和.

  5. 查询:首先找到它的闭区间[s,t](原数组下标+m),然后将它改为开区间,随后s、t一路找爸爸,如果s是左儿子,则加上s的右兄弟,如果t是右儿子,则加上t的左兄弟,当s与t是兄弟时,则退出..

  6. 修改:先找到修改的叶节点x,将它修改,然后把x一路找爸爸,一路上遇到的所有点都修改.

大家可以和上面的图一起对照着看...

再不贴代码会不会打我...

//notes:根据zkw的《统计的力量》,做出来的四不像 
#include <algorithm>   
#include <bitset>     
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>    
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>     
#include <exception>    
#include <fstream>
#include <functional>   
#include <limits>
#include <list>      
#include <map>       
#include <iomanip>
#include <ios>       
#include <iosfwd>     
#include <iostream>
#include <istream>    
#include <ostream>   
#include <queue>    
#include <set>    
#include <sstream>  
#include <stack>      
#include <stdexcept>   
#include <streambuf>  
#include <string>     
#include <utility>    
#include <vector>   
#include <cwchar>
#include <cwctype>
#include <complex.h>  
#include <fenv.h>    
#include <inttypes.h>  
#include <stdbool.h> 
#include <stdint.h>  
#include <tgmath.h>   
#define F(i,a,b) for(int i=a;i<=b;i++)
#define D(i,a,b) for(int i=a;i>=b;i--)
#define MAXN 500050
#define LL long long 
#define INF 0X3F3F3F3F
#define MINF 9999999999999999
using namespace std;
inline bool read(int &num)  
{
        char in;bool IsN=false;
        in=getchar();
        if(in==EOF) return false;
        while(in!='-'&&(in<'0'||in>'9')) in=getchar();
        if(in=='-'){ IsN=true;num=0;}
        else num=in-'0';
        while(in=getchar(),in>='0'&&in<='9')
		{
                num*=10,num+=in-'0';
        }
        if(IsN) num=-num;
        return true;
}
//以上是优美的快读和头文件
int tree[4*MAXN],n,m;
int query(int s,int t )//询问s到t 的闭区间和[左右都取]
{
	int res=0;
	s=t-1,t=t+1;//将它变成开区间(左右都不取)
	s+=m,t+=m;//m是该线段树的叶子层的元素个数.
	while(~(s ^ t)/*s和t不是兄弟*/)
	{
		if(s&1) res+=tree[s^1];//判断s是否为左节点,若是,则加上右节点 
		if(~t&1) res+=tree[t^1];//判断t是否为右节点,若是,则加上左节点 
		s>>=1,t>>=1;//将它改为它的父节点 
	} 
	return res;
} 
void change(int x,int val)
{
	x+=m;//找叶子
	tree[x]=val;//修改它本身
	while(x>1)
	{
		x>>1;//找爸爸
		tree[x]=tree[x<<1]+tree[x<<1^1];//修改爸爸..
	}
}
void init()
{
	read(n);
	for(m=1;m<n+2;m<<=1);
	//求出m 
	F(i,m+1,m+n)
	{
		read(tree[i]);
	}//输入 
	D(i,m-1,1)
	{
		tree[i]=tree[2*i]+tree[2*i+1];
	}//建树 
} 

没了...

留下你的赞!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值