zkw 线段树

ZKW线段树

zkw线段树的特点

·代码短(可手撸)
·常数小
·空间小

原理

与普通线段树不同,zkw线段树是基于满二叉树,自底而上
在这里插入图片描述
区间如下图所示
在这里插入图片描述
查询时,先将闭区间转化为开区间,再不断移动到父亲节点的过程中,将其包含的兄弟节点进行处理,右边界处理右兄弟,左节点处理左兄弟,直到左右区间为兄弟节点

在这里插入图片描述

实现

建树:首先找到单元素所在的层数M,并将当前层数的值初始化。M既是层数,也是当前层数的最大个数,也是前面位置的总数(到这一层0点为止)

		for(M=1;M<=n;M<<=1);
		for(int i=1;i<=M<<1;i++)
			T[i]=0;//T[i]=inf;

添点及单点修改,添加点从位置1开始添加

void modify(int n,int v)//单点加减/修改 
{
	for(T[n+=M]+=v,n>>=1;n;n>>=1)  /*T[n+M]=v*/ 
		T[n]=T[n+n]+T[n+n+1];
}
		for(int i=1,x;i<=n;i++)
		{
			scanf("%d",&x);
			modify(i,x);
		}

查询
1、查询区间和

ll query(int l, int r)
{
	ll ans=0;
	for(l+=M-1,r+=M+1;l^r^1;l>>=1,r>>=1)
	{
		if(~l & 1) ans+=T[l^1];
		if(r & 1) ans+=T[r^1];
	}
	return ans;
}

2、查询区间最大值//最小值同理

ll query(int l, int r)
{
	ll ans=-inf;
	for(l+=M-1,r+=M+1;l^r^1;l>>=1,r>>=1)
	{
		if(~l & 1) ans=max(ans,T[l^1]);
		if(r & 1) ans=max(ans,T[r^1]);
	}
	return ans;
}

单点查询+区间修改

这里虽然是区间修改,但因为单点查询时,会找到父亲节点所有之前加的值,不会漏算。所以就不用lazy了~

void add(int l,int r,int v)//区间加减 
{
	for(l+=M-1,r+=M+1;l^r^1;l>>=1,r>>=1)
	{
		if(~l & 1) T[l^1]+=v;
		if(r & 1) T[r^1]+=v;
	}
}
ll query(int n)//单点查询 
{
	ll ans=0;
	for(n+=M;n;n>>=1) ans+=T[n];
	return ans;
}

区间修改+区间查询(要用lazy标记!)

不用lazy标记会出现漏加的情况,所以用lazy标记修改到根,查询到根。

void add(int L,int R,int v)//区间修改  
{
	L+=M-1,R+=M+1;
	for(int l=L,r=R;l^r^1;l>>=1,r>>=1)
	{
		if(~l & 1) lazy[l^1]+=v,T[l^1]+=v;
		if(r & 1) lazy[r^1]+=v,T[r^1]+=v;
	}
	for(int l=L>>1,r=R>>1;l;l>>=1,r>>=1)
	{
		T[l]=max(T[l+l],T[l+l+1])+lazy[l];
		T[r]=max(T[r+r],T[r+r+1])+lazy[r];
	}
}
int query(int l, int r)//区间查询 
{
	int lmax=-inf,rmax=-inf;
	for(l+=M-1,r+=M+1;l^r^1;l>>=1,r>>=1)
	{
		if(lazy[l]&&lmax!=-inf) lmax+=lazy[l];
		if(lazy[r]&&rmax!=-inf) rmax+=lazy[r];
		if(~l & 1) lmax=max(lmax,T[l^1]);
		if(r & 1) rmax=max(rmax,T[r^1]); 
	}
	for(;l;l>>=1,r>>=1)
	{
		lmax+=lazy[l],rmax+=lazy[r];
	}
	return max(lmax,rmax);
}

区间覆盖+区间查询

(暂时没看懂……)

void modify(int L,int R,int v)//区间覆盖 
{
	L+=M-1,R+=M+1;
	for(int i=(log(M)/log(2))+1,l,r;i;i--)//log(M)/log(2))+1为当前层数
	{
		l=L>>i,r=R>>i;
		if(lazy[l])
		{
			lazy[l+l]=lazy[l+l+1]=lazy[l];
			T[l+l]=T[l+l+1]=lazy[l]*(1<<(i-1));
			lazy[l]=0;
		}
		if(lazy[r])
		{
			lazy[r+r]=lazy[r+r+1]=lazy[r];
			T[r+r]=T[r+r+1]=lazy[r]*(1<<(i-1));
			lazy[r]=0;
		}
	}
	for(int l=L,r=R,num=1;l>1;l>>=1,r>>=1,num<<=1)
	{
		if((l^r^1)>1)
		{
			if(~l & 1) lazy[l^1]=v,T[l^1]=v*num;
			if(r & 1) lazy[r^1]=v,T[r^1]=v*num;
		}
		T[l>>1]=T[l]+T[l^1];
		T[r>>1]=T[r]+T[r^1];
	}
}
int query(int l, int r)
{
	int ansl=0,ansr=0,ln=0,rn=0,nn=1;
	for(l+=M-1,r+=M+1;l^r^1;l>>=1,r>>=1,nn<<=1)
	{
		if(lazy[l]) ansl=lazy[l]*ln;
		if(lazy[r]) ansr=lazy[r]*rn;
		if(~l & 1) ansl+=T[l^1],ln+=nn;
		if(r & 1) ansr+=T[r^1],rn+=nn;
	}
	for(;l;l>>=1,r>>=1)
	{
		if(lazy[l]) ansl=lazy[l]*ln;
		if(lazy[r]) ansr=lazy[r]*rn;
	}
	return ansl+ansr;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值