线 段 树

定义

线段树是以一段区间为节点的树。

设线段树的一个非叶子节点 k k k k k k 的儿子节点为 k < < 1 , k < < 1 ∣ 1 k<<1 ,k<<1|1 k<<1,k<<1∣1

若节点 k k k 表示的是区间 l l l r r r , 则节点 k < < 1 , k < < 1 ∣ 1 k<<1 ,k<<1|1 k<<1,k<<1∣1 分别表示的是区间 l l l m i d mid mid m i d + 1 mid+1 mid+1 r r r

m i d mid mid ( l + r ) > > 1 (l+r)>>1 (l+r)>>1

叶子节点表示的是某一个点的值。

接下来以求动态区间的某一段的和为例。

建树

void build(ll k,ll l,ll r)
{
	if(l==r)//叶子节点
	{
		sum[k]=a[l];
		return;
	}
	else
	{
		int mid=(l+r)/2;//先求出儿子节点的的值,然后该区间的和为两个儿子节点的和
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
		sum[k]=sum[k<<1]+sum[k<<1|1];
	}
}

区间查询,单点修改

ll query(ll k,ll l,ll r,ll x,ll y)
{
	if(l>=x&&r<=y)
	{
		return sum[k];//节点k在查询的区间x至y内
	}
	ll mid=(l+r)/2;
	ll res=0;
	if(x<=mid)res+=query(k<<1,l,mid,x,y);//区间x至y与区间l至mid有交集
	if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);//区间x至y与区间mid+1至r有交集
	return res;
}
void update(ll k,ll l,ll r,ll x,ll y)
{
	if(r<x||l>x)return;
	if(l==r&&l==x)
	{
		sum[x]=y;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) update(k>>1,l,mid,x,y);//同理
	else if(x>mid) update(k>>1|1,mid+1,r,x,y);
	sum[k]=sum[k>>1]+sum[k>>1|1;
	return;
}

区间修改,区间查询(只含加减)

这里引入懒标记-- l a z y — t a g lazy\text{---}tag lazytag

每一次修改先在包含节点 k k k 时,先打上标记,表示该区间加上某一个数值。

如果想要查询某一段区间的值时将懒标记下传至儿子节点。

每一次递归时都要下传懒标记。

inline void push_down(ll k,ll l,ll r,ll mid)
{
	//懒标记下传
	lazy_tag[k<<1]=lazy_tag[k<<1]+lazy_tag[k];
	sum[k<<1]=sum[k<<1]+lazy_tag[k]*(mid-l+1);
	lazy_tag[k<<1|1]=lazy_tag[k<<1|1]+lazy_tag[k];
	sum[k<<1|1]=sum[k<<1|1]+lazy_tag[k]*(r-(mid+1)+1);


	lazy_tag[k]=0;//懒标记变为0
}
void add(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
	if(l>=x&&r<=y)//完全包含
	{
		lazy_tag[k]+=ADD;//加入懒标记
		sum[k]+=(ADD*(r-l+1));//对节点k的值进行修改
		return;
	}
	else
	{
		ll mid=(l+r)>>1;
		push_down(k,l,r,mid);//下传懒标记
		if(x<=mid)add(k<<1,l,mid,x,y,ADD);
		if(y>mid)add(k<<1|1,mid+1,r,x,y,ADD);
		sum[k]=sum[k<<1]+sum[k<<1|1];//重新计算节点k的值
	}
}
ll query(ll k,ll l,ll r,ll x,ll y)
{
	if(l>=x&&r<=y)//完全包含则直接输出节点的值
	{
		return sum[k];
	}
	ll mid=(l+r)/2;
	ll res=0;
	push_down(k,l,r,mid);//下传懒标记
	if(x<=mid)res+=query(k<<1,l,mid,x,y);
	if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
	return res;
}

区间修改,区间查询(含乘法操作)

前提:求的是区间和对一个数字去模后的值。

加入两个懒标记 l a z y t a g 1 lazytag1 lazytag1 , l a z y t a g 2 lazytag2 lazytag2

l a z y t a g 2 lazytag2 lazytag2 的初始值为 1 1 1

l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k] 表示对初始节点(标记下传后的节点 k k k ) 乘上 l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k]

l a z y t a g 1 [ k ] lazytag1[k] lazytag1[k] 表示对初始节点(标记下传后的节点 k k k ) 乘上 l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k] 后加上 l a z y t a g 1 [ k ] lazytag1[k] lazytag1[k]

标记下传时,儿子节点的值先乘上 l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k] ,然后加上 l a z y t a g 1 [ k ] lazytag1[k] lazytag1[k]

儿子节点的 l a z y t a g 2 lazytag2 lazytag2 乘上 l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k]

儿子节点的 l a z y t a g 1 lazytag1 lazytag1 乘上 l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k] ,然后加上 l a z y t a g 1 [ k ] lazytag1[k] lazytag1[k]

注意,先乘后加!!!

inline void push_down(ll k,ll l,ll r,ll mid)
{
	sum[k<<1]=(sum[k<<1]*lazy_tag_cheng[k]+lazy_tag_jia[k]*(mid-l+1))%mod;
	sum[k<<1|1]=(sum[k<<1|1]*lazy_tag_cheng[k]+lazy_tag_jia[k]*(r-mid))%mod;
	lazy_tag_cheng[k<<1]=(lazy_tag_cheng[k<<1]*lazy_tag_cheng[k])%mod;
	lazy_tag_cheng[k<<1|1]=(lazy_tag_cheng[k<<1|1]*lazy_tag_cheng[k])%mod;
	lazy_tag_jia[k<<1]=(lazy_tag_jia[k<<1]*lazy_tag_cheng[k]+lazy_tag_jia[k])%mod;
	lazy_tag_jia[k<<1|1]=(lazy_tag_jia[k<<1|1]*lazy_tag_cheng[k]+lazy_tag_jia[k])%mod;
	lazy_tag_jia[k]=0;
	lazy_tag_cheng[k]=1;
}
void add_cheng(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
	if(l>=x&&r<=y)
	{
		sum[k]*=ADD;
		sum[k]%=mod;
		lazy_tag_cheng[k]=(lazy_tag_cheng[k]*ADD)%mod;
		lazy_tag_jia[k]=(lazy_tag_jia[k]*ADD)%mod;
		return;
	}
	else
	{
		ll mid=(l+r)>>1;
		push_down(k,l,r,mid);
		if(x<=mid)add_cheng(k<<1,l,mid,x,y,ADD);
		if(y>mid)add_cheng(k<<1|1,mid+1,r,x,y,ADD);
		sum[k]=sum[k<<1]+sum[k<<1|1];sum[k]%=mod;
	}
}
void add_jia(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
	if(l>=x&&r<=y)
	{
		lazy_tag_jia[k]=(lazy_tag_jia[k]+ADD)%mod;
		sum[k]+=(ADD*(r-l+1));
		sum[k]%=mod;
		return;
	}
	else
	{
		ll mid=(l+r)>>1;
		push_down(k,l,r,mid);
		if(x<=mid)add_jia(k<<1,l,mid,x,y,ADD);
		if(y>mid)add_jia(k<<1|1,mid+1,r,x,y,ADD);
		sum[k]=sum[k<<1]+sum[k<<1|1];
		sum[k]%=mod;
	}
}
ll query(ll k,ll l,ll r,ll x,ll y)
{
	if(l>=x&&r<=y)
	{
		return sum[k];
	}
	ll mid=(l+r)/2;
	ll res=0;
	push_down(k,l,r,mid);
	if(x<=mid)res+=query(k<<1,l,mid,x,y);
	if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
	return res%mod;
}

例题

P3372 【模板】线段树 1

#include<bits/stdc++.h>
using namespace std;
#define ll long long
unsigned ll n,m;
unsigned ll a[1000001];
unsigned ll sum[4000001];
unsigned ll lazy_tag[4000001];
void build(ll k,ll l,ll r)
{
	lazy_tag[k]=0;
	if(l==r)
	{
		sum[k]=a[l];
		return;
	}
	else
	{
		int mid=(l+r)/2;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
		sum[k]=sum[k<<1]+sum[k<<1|1];
	}
}
inline void push_down(ll k,ll l,ll r,ll mid)
{
	lazy_tag[k<<1]=lazy_tag[k<<1]+lazy_tag[k];
	sum[k<<1]=sum[k<<1]+lazy_tag[k]*(mid-l+1);
	lazy_tag[k<<1|1]=lazy_tag[k<<1|1]+lazy_tag[k];
	sum[k<<1|1]=sum[k<<1|1]+lazy_tag[k]*(r-(mid+1)+1);
	lazy_tag[k]=0;
}
void add(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
	if(l>=x&&r<=y)
	{
		lazy_tag[k]+=ADD;
		sum[k]+=(ADD*(r-l+1));
		return;
	}
	else
	{
		ll mid=(l+r)>>1;
		push_down(k,l,r,mid);
		if(x<=mid)add(k<<1,l,mid,x,y,ADD);
		if(y>mid)add(k<<1|1,mid+1,r,x,y,ADD);
		sum[k]=sum[k<<1]+sum[k<<1|1];
	}
}
ll query(ll k,ll l,ll r,ll x,ll y)
{
	if(l>=x&&r<=y)
	{
		return sum[k];
	}
	ll mid=(l+r)/2;
	ll res=0;
	push_down(k,l,r,mid);
	if(x<=mid)res+=query(k<<1,l,mid,x,y);
	if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
	return res;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(ll i=1; i<=n; i++)
		scanf("%lld",&a[i]);
	build(1,1,n);
	for(ll i=1; i<=m; i++)
	{
		ll op,x,y;
		scanf("%lld%lld%lld",&op,&x,&y);
		if(op==2)
		{
			printf("%lld\n",query(1,1,n,x,y));
		}
		else
		{
			ll k;
			scanf("%lld",&k);
			add(1,1,n,x,y,k);
		}
	}
}

P3373 【模板】线段树 2

#include<bits/stdc++.h>
using namespace std;
#define ll long long
unsigned ll n,m;
unsigned ll a[1000001];
unsigned ll sum[4000001];
unsigned ll lazy_tag_cheng[4000001];
unsigned ll lazy_tag_jia[4000001],mod;
void build(ll k,ll l,ll r)
{
	lazy_tag_jia[k]=0;
	lazy_tag_cheng[k]=1;
	if(l==r)
	{
		sum[k]=a[l];
		sum[k]%=mod;
		return;
	}
	else
	{
		int mid=(l+r)/2;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
		sum[k]=sum[k<<1]+sum[k<<1|1];
		sum[k]%=mod;
	}
}
inline void push_down(ll k,ll l,ll r,ll mid)
{
	sum[k<<1]=(sum[k<<1]*lazy_tag_cheng[k]+lazy_tag_jia[k]*(mid-l+1))%mod;
	sum[k<<1|1]=(sum[k<<1|1]*lazy_tag_cheng[k]+lazy_tag_jia[k]*(r-mid))%mod;
	lazy_tag_cheng[k<<1]=(lazy_tag_cheng[k<<1]*lazy_tag_cheng[k])%mod;
	lazy_tag_cheng[k<<1|1]=(lazy_tag_cheng[k<<1|1]*lazy_tag_cheng[k])%mod;
	lazy_tag_jia[k<<1]=(lazy_tag_jia[k<<1]*lazy_tag_cheng[k]+lazy_tag_jia[k])%mod;
	lazy_tag_jia[k<<1|1]=(lazy_tag_jia[k<<1|1]*lazy_tag_cheng[k]+lazy_tag_jia[k])%mod;
	lazy_tag_jia[k]=0;
	lazy_tag_cheng[k]=1;
}
void add_cheng(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
	if(l>=x&&r<=y)
	{
		sum[k]*=ADD;
		sum[k]%=mod;
		lazy_tag_cheng[k]=(lazy_tag_cheng[k]*ADD)%mod;
		lazy_tag_jia[k]=(lazy_tag_jia[k]*ADD)%mod;
		return;
	}
	else
	{
		ll mid=(l+r)>>1;
		push_down(k,l,r,mid);
		if(x<=mid)add_cheng(k<<1,l,mid,x,y,ADD);
		if(y>mid)add_cheng(k<<1|1,mid+1,r,x,y,ADD);
		sum[k]=sum[k<<1]+sum[k<<1|1];sum[k]%=mod;
	}
}
void add_jia(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
	if(l>=x&&r<=y)
	{
		lazy_tag_jia[k]=(lazy_tag_jia[k]+ADD)%mod;
		sum[k]+=(ADD*(r-l+1));
		sum[k]%=mod;
		return;
	}
	else
	{
		ll mid=(l+r)>>1;
		push_down(k,l,r,mid);
		if(x<=mid)add_jia(k<<1,l,mid,x,y,ADD);
		if(y>mid)add_jia(k<<1|1,mid+1,r,x,y,ADD);
		sum[k]=sum[k<<1]+sum[k<<1|1];
		sum[k]%=mod;
	}
}
ll query(ll k,ll l,ll r,ll x,ll y)
{
	if(l>=x&&r<=y)
	{
		return sum[k];
	}
	ll mid=(l+r)/2;
	ll res=0;
	push_down(k,l,r,mid);
	if(x<=mid)res+=query(k<<1,l,mid,x,y);
	if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
	return res%mod;
}
signed main()
{
	scanf("%lld%lld%lld",&n,&m,&mod);
	for(ll i=1; i<=n; i++)
		scanf("%lld",&a[i]);
	build(1,1,n);
	for(ll i=1; i<=m; i++)
	{
		ll op,x,y;
		scanf("%lld%lld%lld",&op,&x,&y);
		if(op==3)
		{
			printf("%lld\n",query(1,1,n,x,y));
		}
		else
		if(op==1)
		{
			ll k;
			scanf("%lld",&k);
			add_cheng(1,1,n,x,y,k);
		}
		else
		if(op==2)
		{
			ll k;scanf("%lld",&k);
			add_jia(1,1,n,x,y,k);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值