P3373 【模板】线段树 2(区间乘法+区间加法+区间求和)

题目描述
如题,已知一个数列,你需要进行下面三种操作:

将某区间每一个数乘上 xx

将某区间每一个数加上 xx

求出某区间每一个数的和

输入格式
第一行包含三个整数 n,m,pn,m,p,分别表示该数列数字的个数、操作的总个数和模数。

第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 mm 行每行包含若干个整数,表示一个操作,具体如下:

操作 11: 格式:1 x y k 含义:将区间 [x,y][x,y] 内每个数乘上 kk

操作 22: 格式:2 x y k 含义:将区间 [x,y][x,y] 内每个数加上 kk

操作 33: 格式:3 x y 含义:输出区间 [x,y][x,y] 内每个数的和对 pp 取模所得的结果

输出格式
输出包含若干行整数,即为所有操作 33 的结果。
思路:虽然多了一个区间乘法,但是还是蛮难做的。今天才发现我一开始学线段树的时候做过这道题目,但是在后面的比赛训练种,并没有接触过区间乘法的题目,就给忘的一干二净,写篇博客记录一下。
需要额外注意的地方:
①在pushdown的过程中,我们在计算左右子树和的时候,采用乘法优先的原则,即规定好segtree[root* 2].value=(segtree[root* 2].value* segtree[root].mul+segtree[root].add*(本区间长度))%p。而不是加法优先的原则。
②在对区间进行加一个数的时候,没有额外需要注意的;但是当区间乘以一个数的时候,这个区间之前加的值,也需要额外的乘以这个数字。
具体解释看代码:
这个代码完全可以当作模板了^ _ ^

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int maxx=1e5+100;
struct node{
	int l,r;
	ll sum,lazyx,lazyj;
}p[maxx<<2];
int n,m,q;
ll a[maxx];

inline void pushup(int cur)
{
	p[cur].sum=(p[cur<<1].sum+p[cur<<1|1].sum)%q; 
}
inline void pushdown(int cur)
{
	p[cur<<1].sum=(p[cur<<1].sum*p[cur].lazyx+(ll)(p[cur<<1].r-p[cur<<1].l+1)*p[cur].lazyj)%q;
	p[cur<<1|1].sum=(p[cur<<1|1].sum*p[cur].lazyx+(ll)(p[cur<<1|1].r-p[cur<<1|1].l+1)*p[cur].lazyj)%q;//子节点的和=子节点的和乘以父节点的乘法懒惰标记+父节点的加法懒惰标记*当前区间的长度
	p[cur<<1].lazyj=(p[cur<<1].lazyj*p[cur].lazyx+p[cur].lazyj)%q;
	p[cur<<1|1].lazyj=(p[cur<<1|1].lazyj*p[cur].lazyx+p[cur].lazyj)%q;//加法的懒惰标记需要乘以父节点的乘法懒惰标记再加上父节点的加法懒惰标记
	p[cur<<1].lazyx=(p[cur<<1].lazyx*p[cur].lazyx)%q;
	p[cur<<1|1].lazyx=(p[cur<<1|1].lazyx*p[cur].lazyx)%q;
	p[cur].lazyj=0;
	p[cur].lazyx=1;
}
inline void build(int l,int r,int cur)
{
	p[cur].l=l;
	p[cur].r=r;
	p[cur].lazyj=0;
	p[cur].lazyx=1;
	if(l==r)
	{
		p[cur].sum=(a[l])%q;
		return ;
	}
	int mid=l+r>>1;
	build(l,mid,cur<<1);
	build(mid+1,r,cur<<1|1);
	pushup(cur);
}
inline void updatej(int l,int r,int cur,ll x)//更新加法
{
	int L=p[cur].l;
	int R=p[cur].r;
	if(l<=L&&R<=r)
	{
		p[cur].lazyj+=x;
		p[cur].lazyj%=q;
		p[cur].sum+=(ll)(p[cur].r-p[cur].l+1)*x;
		p[cur].sum%=q;
		return ;
	}
	pushdown(cur);
	int mid=L+R>>1;
	if(r<=mid) updatej(l,r,cur<<1,x);
	else if(l>mid) updatej(l,r,cur<<1|1,x);
	else updatej(l,mid,cur<<1,x),updatej(mid+1,r,cur<<1|1,x);
	pushup(cur); 
}
inline void updatex(int l,int r,int cur,ll x)//更新乘法
{
	int L=p[cur].l;
	int R=p[cur].r;
	if(l<=L&&R<=r)
	{
		p[cur].sum=(p[cur].sum*x)%q;
		p[cur].lazyx=(p[cur].lazyx*x)%q;
		p[cur].lazyj=(p[cur].lazyj*x)%q;
		return ;
	}
	pushdown(cur);
	int mid=L+R>>1;
	if(r<=mid) updatex(l,r,cur<<1,x);
	else if(l>mid) updatex(l,r,cur<<1|1,x);
	else updatex(l,mid,cur<<1,x),updatex(mid+1,r,cur<<1|1,x);
	pushup(cur);
}
inline ll query(int l,int r,int cur)//求和
{
	int L=p[cur].l;
	int R=p[cur].r;
	if(l<=L&&R<=r) return p[cur].sum%q;
	pushdown(cur);
	int mid=L+R>>1;
	if(r<=mid) return query(l,r,cur<<1)%q;
	else if(l>mid) return query(l,r,cur<<1|1)%q;
	else return (query(l,mid,cur<<1)%q+query(mid+1,r,cur<<1|1)%q)%q;
}
int main()
{
	while(~scanf("%d%d%d",&n,&m,&q))
	{
		for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
		build(1,n,1);
		int x,y,z;ll k;
		while(m--)
		{
			scanf("%d",&x);
			if(x==1)
			{
				scanf("%d%d%lld",&y,&z,&k);
				updatex(y,z,1,k);
			}
			else if(x==2)
			{
				scanf("%d%d%lld",&y,&z,&k);
				updatej(y,z,1,k);
			}
			else
			{
				scanf("%d%d",&y,&z);
				printf("%lld\n",query(y,z,1)%q);
			} 
		}
		
	}
	return 0;
}

努力加油a啊

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

starlet_kiss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值