【Ybtoj 第17章例4】维护序列【线段树】

这篇博客介绍了如何使用树状数组(也称作线段树)结合懒惰标记法来高效地处理区间乘法和加法操作。文章详细阐述了懒惰标记的原理,并给出了C++代码实现,包括区间乘法、区间加法和区间查询的功能。通过对树节点的下传标记操作,实现了复杂度为O(logn)的区间更新和查询操作。
摘要由CSDN通过智能技术生成

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


解题思路

我们考虑用懒标记,当对一个区间打标记时要保证以先乘后加的形式,mul表示乘多少,pul表示加多少,sum表示区间和。
下传标记时,

  • s u m [ l s o n ] ∗ = m u l [ f a ] , s u m [ r s o n ] ∗ = m u l [ f a ] , s u m [ l s o n ] + = p u l [ f a ] ∗ ( m i d − l + 1 ) , s u m [ r s o n ] + = p u l [ f a ] ∗ ( r − m i d ) sum[lson]*=mul[fa],sum[rson]*=mul[fa],sum[lson]+=pul[fa]*(mid-l+1),sum[rson]+=pul[fa]*(r-mid) sum[lson]=mul[fa],sum[rson]=mul[fa],sum[lson]+=pul[fa](midl+1),sum[rson]+=pul[fa](rmid)
  • m u l [ l s o n ] ∗ = m u l [ f a ] , m u l [ r s o n ] ∗ = m u l [ f a ] mul[lson]*=mul[fa],mul[rson]*=mul[fa] mul[lson]=mul[fa],mul[rson]=mul[fa]
  • p u l [ l s o n ] ∗ = m u l [ f a ] , p u l [ r s o n ] ∗ = m u l [ f a ] , p u l [ l s o n ] + = p u l [ f a ] , p u l [ r s o n ] + = p u l [ f a ] pul[lson]*=mul[fa],pul[rson]*=mul[fa],pul[lson]+=pul[fa],pul[rson]+=pul[fa] pul[lson]=mul[fa],pul[rson]=mul[fa],pul[lson]+=pul[fa],pul[rson]+=pul[fa]

代码

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;

int mod,n,m,k,xx,yy,zz,a[500010];

struct c {
	long long num,mul,plu;
} tree[2000010];


void build(int k,int l,int r) {
	tree[k].mul=1;
	if(l==r) {
		tree[k].num=a[l]%mod;
		return;
	}
	int mid=(l+r)/2;
	build(k*2,l,mid);
	build(k*2+1,mid+1,r);
	tree[k].num=tree[k*2].num+tree[k*2+1].num;
	tree[k].num%=mod;
}

void down(int k,int l,int r,int mid) {
	int k1=k*2,k2=k*2+1;
	tree[k1].num*=tree[k].mul;
	tree[k1].num%=mod;
	tree[k1].num+=tree[k].plu*(mid-l+1);
	tree[k1].num%=mod;
	tree[k2].num*=tree[k].mul;
	tree[k2].num%=mod;
	tree[k2].num+=tree[k].plu*(r-mid);
	tree[k2].num%=mod;

	tree[k1].mul*=tree[k].mul;
	tree[k1].mul%=mod;
	tree[k2].mul*=tree[k].mul;
	tree[k2].mul%=mod;

	tree[k1].plu*=tree[k].mul;
	tree[k1].plu%=mod;
	tree[k1].plu+=tree[k].plu;
	tree[k1].plu%=mod;
	tree[k2].plu*=tree[k].mul;
	tree[k2].plu%=mod;
	tree[k2].plu+=tree[k].plu;
	tree[k2].plu%=mod;
	tree[k].plu=0,tree[k].mul=1;
}

void change_mul(int k,int l,int r,int x,int y,int v) {
	if(l>=x&&r<=y) {
		tree[k].num*=v;
		tree[k].num%=mod;
		tree[k].mul*=v;
		tree[k].mul%=mod;
		tree[k].plu*=v;
		tree[k].plu%=mod;
		return;
	}
	int mid=(l+r)/2;
	down(k,l,r,mid);
	if(x<=mid)change_mul(k*2,l,mid,x,y,v);
	if(y>mid)change_mul(k*2+1,mid+1,r,x,y,v);
	tree[k].num=tree[k*2].num+tree[k*2+1].num;
	tree[k].num%=mod;
}

void change_plu(int k,int l,int r,int x,int y,int v) {
	if(l>=x&&r<=y) {
		tree[k].num=tree[k].num+v*(r-l+1)%mod;
		tree[k].num%=mod;
		tree[k].plu+=v;
		tree[k].plu%=mod;
		return;
	}
	int mid=(l+r)/2;
	down(k,l,r,mid);
	if(x<=mid)change_plu(k*2,l,mid,x,y,v);
	if(y>mid)change_plu(k*2+1,mid+1,r,x,y,v);
	tree[k].num=tree[k*2].num+tree[k*2+1].num;
	tree[k].num%=mod;
}

long long query(int k,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	return tree[k].num;
	int mid=(l+r)/2;
	down(k,l,r,mid);
	long long res=0;
	if(x<=mid)res=(res+query(k*2,l,mid,x,y))%mod;
	if(y>mid)res=(res+query(k*2+1,mid+1,r,x,y))%mod;
	return res;
}

int main() {
	scanf("%d%d",&n,&mod);
	for(int i=1; i<=n; i++)
		scanf("%d",&a[i]);
	build(1,1,n);
	scanf("%d",&m);
	for(int i=1; i<=m; i++) {
		scanf("%d",&k);
		if(k==1) {
			scanf("%d%d%d",&xx,&yy,&zz);
			change_mul(1,1,n,xx,yy,zz);
		}
		if(k==2) {
			scanf("%d%d%d",&xx,&yy,&zz);
			change_plu(1,1,n,xx,yy,zz);
		}
		if(k==3) {
			scanf("%d%d",&xx,&yy);
			printf("%lld\n",query(1,1,n,xx,yy));
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值