线段树模板及其运用

本文解析了洛谷平台上的两道经典线段树题目:P2068统计和与P3373线段树2。通过示例代码详细介绍了线段树的基本操作如构建、更新和查询,并针对P3373特别讨论了如何处理乘法和加法标签的传递。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录:

洛谷 P2068 统计和
洛谷 P3373 线段树2

###洛谷 P2068 统计和
很裸的一道模板题,直接上代码了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((tree[rt].r+tree[rt].l)>>1)
#define fp(i,a,b) for(register int i=a;i<=b;++i)
using namespace std;
struct arr{
	ll l,r,sum,tag,tag1;
}tree[500000];
int n,m;
inline ll read(){
	int x=0,w=1;char ch=0;
	while(ch!='0'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') w=-1,ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
	return x*w;
}
inline void pushup(int rt){tree[rt].sum=(tree[ls].sum+tree[rs].sum);}
inline void pushdown(int rt){
	if(tree[rt].tag){
		tree[ls].tag+=tree[rt].tag;
		tree[rs].tag+=tree[rt].tag;
		tree[ls].sum=tree[ls].sum+((tree[ls].r-tree[ls].l+1)*tree[rt].tag);
		tree[rs].sum=tree[rs].sum+((tree[rs].r-tree[rs].l+1)*tree[rt].tag);
		tree[rt].tag=0; 
	}
}
void build(int l,int r,int rt) {
	tree[rt].l=l;tree[rt].r=r;tree[rt].tag1=1;
	if(l==r) return ;
	int midd=(l+r)>>1;
	build(l,midd,ls);build(midd+1,r,rs);
	pushup(rt);
} 
void updata(int l,int r,ll c,int rt) {
	if(l==tree[rt].l&&tree[rt].r==r) {
		tree[rt].sum=(tree[rt].sum+(tree[rt].r-tree[rt].l+1)*c); 
		tree[rt].tag+=c;
		return ; 
	}
	pushdown(rt);
	if(r<=mid) updata(l,r,c,ls);
	else if(l>mid) updata(l,r,c,rs);
		 else {updata(l,mid,c,ls);updata(mid+1,r,c,rs); }
	pushup(rt);
}
ll query(int l,int r,int rt) {
	if(l==tree[rt].l&&tree[rt].r==r) return tree[rt].sum;
	pushdown(rt);
	ll ans=0;
	if(r<=mid) ans+=query(l,r,ls);
	else if(l>mid) ans+=query(l,r,rs);
		 else {ans+=query(l,mid,ls);ans+=query(mid+1,r,rs); }
	pushup(rt);
	return ans;
}
int main(){
	n=read();m=read();
	build(1,n,1);
	fp(i,1,m) {
		char op;cin>>op; 
		if(op=='x'){int l=read(),k=read();updata(l,l,k,1);}
		if(op=='y'){int l=read(),r=read();printf("%lld\n",query(l,r,1));}
	}
} 

###洛谷 P3373 线段树2
这里有几个注意点:
1.我们要下传两个tag,一个用于乘法,另一个用于加法
2.我们在下传乘法的tag时,同时要更新加法的tag。
3.我们在下传tag的时候,必须得先更新乘法的,在更新加法的。
4.具体看代码吧(自我感觉代码可读性还可以的吧)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((tree[rt].r+tree[rt].l)>>1)
#define fp(i,a,b) for(register int i=a;i<=b;++i)
using namespace std;
struct arr{
	ll l,r,sum,tag,tag1;
}tree[500000];
int n,m,p;
inline ll read(){
	int x=0,w=1;char ch=0;
	while(ch!='0'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') w=-1,ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
	return x*w;
}
inline void pushup(int rt){tree[rt].sum=(tree[ls].sum+tree[rs].sum)%p;}
inline void pushdown(int rt){
	if(tree[rt].tag1!=1){
		tree[ls].tag1*=tree[rt].tag1;tree[ls].tag1%=p;
		tree[rs].tag1*=tree[rt].tag1;tree[rs].tag1%=p;
		tree[ls].tag*=tree[rt].tag1;tree[ls].tag%=p;
		tree[rs].tag*=tree[rt].tag1;tree[rs].tag%=p;
		tree[ls].sum=tree[ls].sum*tree[rt].tag1;tree[ls].sum%=p;
		tree[rs].sum=tree[rs].sum*tree[rt].tag1;tree[rs].sum%=p;
		tree[rt].tag1=1; 
	}
	if(tree[rt].tag){
		tree[ls].tag+=tree[rt].tag;tree[ls].tag%=p;
		tree[rs].tag+=tree[rt].tag;tree[rs].tag%=p;
		tree[ls].sum=tree[ls].sum+((tree[ls].r-tree[ls].l+1)*tree[rt].tag%p);
		tree[rs].sum=tree[rs].sum+((tree[rs].r-tree[rs].l+1)*tree[rt].tag%p);
		tree[rt].tag=0; 
	}
}
void build(int l,int r,int rt) {
	tree[rt].l=l;tree[rt].r=r;tree[rt].tag1=1;
	if(l==r) return ;
	int midd=(l+r)>>1;
	build(l,midd,ls);build(midd+1,r,rs);
	pushup(rt);
} 
void updata(int l,int r,ll c,int rt) {
	if(l==tree[rt].l&&tree[rt].r==r) {
		tree[rt].sum=(tree[rt].sum+(tree[rt].r-tree[rt].l+1)*c%p); 
		tree[rt].tag+=c;tree[rt].tag%=p;
		return ; 
	}
	pushdown(rt);
	if(r<=mid) updata(l,r,c,ls);
	else if(l>mid) updata(l,r,c,rs);
		 else {updata(l,mid,c,ls);updata(mid+1,r,c,rs); }
	pushup(rt);
}
void updata1(int l,int r,ll c,int rt) {
	if(l==tree[rt].l&&tree[rt].r==r) {
		tree[rt].sum=(tree[rt].sum*c)%p; 
		tree[rt].tag1*=c;tree[rt].tag1%=p;
		tree[rt].tag*=c;tree[rt].tag%=p;
		return ; 
	}
	pushdown(rt);
	if(r<=mid) updata1(l,r,c,ls);
	else if(l>mid) updata1(l,r,c,rs);
		 else {updata1(l,mid,c,ls);updata1(mid+1,r,c,rs); }
	pushup(rt);
} 
ll query(int l,int r,int rt) {
	if(l<=tree[rt].l&&tree[rt].r<=r) return tree[rt].sum;
	pushdown(rt);
	ll ans=0;
	if(l<=mid) ans+=query(l,r,ls),ans%=p;
	if(r>mid) ans+=query(l,r,rs),ans%=p;
	pushup(rt);
	return ans;
}
int main(){
	n=read();m=read();p=read();
	build(1,n,1);
	fp(i,1,n) { int x=read();updata(i,i,x,1);}
	fp(i,1,m) {
		int op=read();
		if(op==1){	int l=read(),r=read(),k=read();	updata1(l,r,k,1);}
		if(op==2){	int l=read(),r=read(),k=read();	updata(l,r,k,1);}
		if(op==3){	int l=read(),r=read(); printf("%lld\n",query(l,r,1));}
	}
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值