洛谷(P3373)线段树加乘混合模板

题目链接:P3373 【模板】线段树 2 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这道题目的意思很明确,就是要我们在线完成区间的乘和加运算并支持查询区间和的一个问题。处理这道题目之前一定要对线段树有一定的了解,如果对线段树还不理解的小伙伴可以看下我之前的博客,在这我附上地址:(17条消息) 线段树模板_AC__dream的博客-CSDN博客

现在来对这道题目做出分析,我们知道涉及到线段树区间修改问题必然会使用懒标记,知道了线段树区间加法的小伙伴肯定会自然而然地想到使用加法懒标记和乘法懒标记来解决这道题目,但是需要注意的是加法懒标记和乘法懒标记并不是独立向下传送的,举个例子,为了便于大家理解,我就拿一个长度为1初始值也为1的区间来举例吧,我们先乘5再加10再乘6再乘7再加6再乘3,这样我们就应该得到这个区间的值为((( 1 * 5 )+ 10 )* 6 * 7 + 6 )* 3 = 1908,但如果我们把加法标记和乘法标记两个标记单独来看就应该得到乘法标记是630,加法标记是16,这样最后结果无论如何也算不对了,那问题到底出在哪了呢?我们注意到乘法标记会对加法标记产生一定的影响,先看乘5再加10再乘6这步操作,我们会发现( 1 * 5 )+ 10 )* 6=1*5*6+10*6=1*5*6+60,我们注意到加法标记应该变为60,也就是说每出现一次乘法操作都会对之前已有的加法标记产生一定的影响,我们知道了这个再看原来的操作就容易发现

((( 1 * 5 )+ 10 )* 6 * 7 + 6 )* 3=1*5*6*7*3+10*6*7*3+6*3,就是每一个乘法操作出现时,在他之前出现的加法操作就要对应地乘上相应的倍数,这也就是本道题目的难点所在了,其他就是基本的线段树模板了,下面上代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=5e5+10;
long long sum[N],sumlazy[N],mullazy[N],l[N],r[N],a[N];
int mod;
void pushup(int id)
{
	sum[id]=(sum[id<<1]+sum[id<<1|1])%mod;
}
void pushdown(int id)
{
	if(mullazy[id]!=1)
	{
		//父区间的乘法标记会对子区间的加法标记产生影响
		sumlazy[id<<1]=sumlazy[id<<1]*mullazy[id]%mod;
		sumlazy[id<<1|1]=sumlazy[id<<1|1]*mullazy[id]%mod;
		sum[id<<1]=(sum[id<<1]*mullazy[id])%mod;
		sum[id<<1|1]=(sum[id<<1|1]*mullazy[id])%mod;
		mullazy[id<<1]=(mullazy[id<<1]*mullazy[id])%mod;
		mullazy[id<<1|1]=(mullazy[id<<1|1]*mullazy[id])%mod;
		mullazy[id]=1;
	}
	if(sumlazy[id])
	{
		sumlazy[id<<1]+=sumlazy[id];
		sumlazy[id<<1|1]+=sumlazy[id];
		sum[id<<1]=(sum[id<<1]+(r[id<<1]-l[id<<1]+1)*sumlazy[id])%mod;
		sum[id<<1|1]=(sum[id<<1|1]+(r[id<<1|1]-l[id<<1|1]+1)*sumlazy[id])%mod;
		sumlazy[id]=0;
	}
}
void build(int id,int L,int R)
{
	sum[id]=0;l[id]=L;r[id]=R;sumlazy[id]=0;mullazy[id]=1;
	if(L==R)
	{
		sum[id]=a[L];
		return ;
	}
	int mid=L+R>>1;
	build(id<<1,L,mid);
	build(id<<1|1,mid+1,R);
	pushup(id);
}
void sumupdate_interval(int id,int L,int R,int val)
{
	if(l[id]>R||r[id]<L) return ;
	if(l[id]>=L&&r[id]<=R)
	{
		sumlazy[id]+=val;
		sum[id]=(sum[id]+(r[id]-l[id]+1)*val)%mod;
		return ;
	}
	pushdown(id);
	sumupdate_interval(id<<1,L,R,val);
	sumupdate_interval(id<<1|1,L,R,val);
	pushup(id);
}
void mulupdate_interval(int id,int L,int R,int val)
{
	if(l[id]>R||r[id]<L) return ;
	if(l[id]>=L&&r[id]<=R)
	{
		sumlazy[id]=(sumlazy[id]*val)%mod;//当前的乘法操作会影响之前已经产生加法标记 
		mullazy[id]=val%mod*mullazy[id]%mod;
		sum[id]=val%mod*sum[id]%mod;
		return ;
	}
	pushdown(id);
	mulupdate_interval(id<<1,L,R,val);
	mulupdate_interval(id<<1|1,L,R,val);
	pushup(id);
}
long long query_interval(int id,int L,int R)
{
	if(l[id]>R||r[id]<L) return 0;
	if(l[id]>=L&&r[id]<=R) return sum[id];
	pushdown(id);
	return (query_interval(id<<1,L,R)+query_interval(id<<1|1,L,R))%mod;
}
int main()
{
	int n,m;
	cin>>n>>m>>mod;
	int op,x,y,k;
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	build(1,1,n);
	while(m--)
	{
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d%d",&x,&y,&k);
			mulupdate_interval(1,x,y,k);
		}
		else if(op==2)
		{
			scanf("%d%d%d",&x,&y,&k);
			sumupdate_interval(1,x,y,k);
		}
		else
		{
			scanf("%d%d",&x,&y);
			printf("%lld\n",query_interval(1,x,y));
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值