LuoguP3373 【模板】线段树 2

这道题虽然是模板题,但对作为线段树萌新的我来说很不友好QAQ

主要是对乘法、加法哪个的懒惰标记先处理这个问题的解决:(懒得写了,引用自题解)

  1. 加法优先,即规定好 :

tree2root=(tree2root+markA,root)markM,root mod p

问题是这样的话非常不容易进行更新操作,假如改变一下 add 的数值, mul 也要联动变成奇奇怪怪的分数小数损失精度。

  1. 乘法优先,即规定好 ( len 是区间长度 ):

tree2root=(tree2rootmarkM,root+markA,rootlen)) mod p

这样的话假如改变 add 的数值就只改变 add ,改变 mul 的时候把 add 也对应的乘一下就可以了,没有精度损失,看起来很不错。

注:上文 mark 的第一个角标 A 指 add (加), M 指 multiple (乘) 。

剩下的便不难了,但是注意一下这个问题!:

i×2+1 = i<<1|1  i<<1+1

这是因为 加法 优先度比 按位或 高。所以请使用一种码风,不要混用

代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+1;
int n,m,P,A[N],tree[N*4];
int markA[N*4],markM[N*4];
void dfs(int l=1,int r=n,int root=1){
	markM[root]=1;
	if(l==r){tree[root]=A[l]%P;return ;}
	int mid=(l+r)>>1;
	dfs(l,mid,root<<1);
	dfs(mid+1,r,root<<1|1);
	tree[root]=(tree[root<<1]+tree[root<<1|1])%P;
}
void pushdown(int p,int l,int r){
	int len=r-l+1;
	tree[p<<1]=(tree[p<<1]*markM[p]+markA[p]*(len-len/2))%P;
	tree[p<<1|1]=(tree[p<<1|1]*markM[p]+markA[p]*(len/2))%P;
	markM[p<<1]=markM[p<<1]*markM[p]%P;
	markM[p<<1|1]=markM[p<<1|1]*markM[p]%P;
	markA[p<<1]=(markA[p<<1]*markM[p]+markA[p])%P;
	markA[p<<1|1]=(markA[p<<1|1]*markM[p]+markA[p])%P;
	markM[p]=1;
	markA[p]=0;
}
inline void UpdA(int d,int L,int R,int l=1,int r=n,int p=1){
	if(L>r||l>R)return ;
	if(L<=l&&r<=R){
		tree[p]=(tree[p]+d*(r-l+1))%P;
		if(r>l)markA[p]=(markA[p]+d)%P;
		return ;
	}int mid=(l+r)>>1;
	pushdown(p,l,r);
	UpdA(d,L,R,mid+1,r,p<<1|1);
	UpdA(d,L,R,l,mid,p<<1);
	tree[p]=(tree[p<<1]+tree[p<<1|1])%P;
}
void UpdM(int d,int L,int R,int l=1,int r=n,int p=1){
	if(L>r||l>R)return ;
	if(L<=l&&r<=R){
		tree[p]=tree[p]*d%P;
		if(r>l){
			markM[p]=markM[p]*d%P;
			markA[p]=markA[p]*d%P;
		}return ;
	}int mid=(l+r)>>1;
	pushdown(p,l,r);
	UpdM(d,L,R,mid+1,r,p<<1|1);
	UpdM(d,L,R,l,mid,p<<1);
	tree[p]=(tree[p<<1]+tree[p<<1|1])%P;
}
int query(int L,int R,int l=1,int r=n,int p=1){
	if(L>r||l>R)return 0;
	if(L<=l&&r<=R)return tree[p]%P;
	int mid=(l+r)>>1;
	pushdown(p,l,r);
	return (query(L,R,mid+1,r,p<<1|1)+query(L,R,l,mid,p<<1))%P;
}
signed main(){
	scanf("%lld%lld%lld",&n,&m,&P);
	for(int i=1;i<=n;i++)scanf("%lld",A+i);
	dfs();
	for(int i=1;i<=m;i++){
		int o,x,y,k;
		scanf("%lld%lld%lld",&o,&x,&y);
		if(o!=3){
			scanf("%lld",&k);
			if(o==2)UpdA(k,x,y);
			else UpdM(k,x,y);
		}else printf("%lld\n",query(x,y));
	}
	return 0;
}
posted @   robinyqc  阅读(19)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示