[AHOI2009]维护序列,洛谷之提高历练地,线段树树状数组基础

本文提供了一道AHOI2009竞赛中关于维护序列问题的详细解答,采用线段树作为主要数据结构,并通过懒标记优化操作。介绍了如何实现加法和乘法操作,以及如何获取区间总和。

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

正题

      第三题:[AHOI2009]维护序列

      这道题是线段树的模板,只要用lazy记录一下即可,注意处理两个lazy(加和乘)的时候,注意运用乘法分配律。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#define LL long long

int n,m;
LL mod;
struct tree{
	int x,y,ls,rs;
	LL c,lzm,lza;
}s[200010];
int len=0;

void pushdown(int p){
	int ls=s[p].ls,rs=s[p].rs;
	s[ls].lzm=s[ls].lzm*s[p].lzm%mod;
	s[rs].lzm=s[rs].lzm*s[p].lzm%mod;
	s[ls].lza=(s[ls].lza*s[p].lzm%mod+s[p].lza)%mod;
	s[rs].lza=(s[rs].lza*s[p].lzm%mod+s[p].lza)%mod;
	s[ls].c=(s[ls].c*s[p].lzm%mod+(s[ls].y-s[ls].x+1)*s[p].lza%mod)%mod;
	s[rs].c=(s[rs].c*s[p].lzm%mod+(s[rs].y-s[rs].x+1)*s[p].lza%mod)%mod;
	s[p].lzm=1;s[p].lza=0;
	s[p].c=s[s[p].ls].c+s[s[p].rs].c;
}

void bt(int x,int y){
	len++;
	s[len].x=x;s[len].y=y;
	s[len].ls=s[len].rs=-1;
	s[len].c=0;
	s[len].lzm=1;s[len].lza=0;
	if(x==y) return ;
	int i=len,mid=(x+y)/2;
	s[i].ls=len+1;bt(x,mid);
	s[i].rs=len+1;bt(mid+1,y);
}

void mult(int p,int x,int y,LL op){
	if(s[p].x==x && s[p].y==y){
		s[p].lzm=s[p].lzm*op%mod;
		s[p].lza=s[p].lza*op%mod;
		s[p].c=s[p].c*op%mod;
		return ;
	}
	pushdown(p);
	int mid=s[s[p].ls].y;
	if(y<=mid) mult(s[p].ls,x,y,op);
	else if(x>mid) mult(s[p].rs,x,y,op);
	else mult(s[p].ls,x,mid,op),mult(s[p].rs,mid+1,y,op);
	s[p].c=(s[s[p].ls].c+s[s[p].rs].c)%mod;
}

void add(int p,int x,int y,LL op){
	if(s[p].x==x && s[p].y==y){
		s[p].lza=(s[p].lza+op)%mod;
		s[p].c=(s[p].c+op*(s[p].y-s[p].x+1)%mod)%mod;
		return ;
	}
	pushdown(p);
	int mid=s[s[p].ls].y;
	if(y<=mid) add(s[p].ls,x,y,op);
	else if(x>mid) add(s[p].rs,x,y,op);
	else add(s[p].ls,x,mid,op),add(s[p].rs,mid+1,y,op);
	s[p].c=(s[s[p].ls].c+s[s[p].rs].c)%mod;
}

LL get_tot(int p,int x,int y){
	if(s[p].x==x && s[p].y==y)
		return s[p].c;
	pushdown(p);
	int mid=s[s[p].ls].y;
	if(y<=mid) return get_tot(s[p].ls,x,y);
	else if(x>mid) return get_tot(s[p].rs,x,y);
	else return (get_tot(s[p].ls,x,mid)+get_tot(s[p].rs,mid+1,y))%mod;
}

int main(){
	scanf("%d %lld",&n,&mod);
	bt(1,n);
	for(int i=1;i<=n;i++) {
		int x;
		scanf("%d",&x);
		add(1,i,i,x);
	}
	scanf("%d",&m);
	int k,x,y;
	LL op;
	for(int i=1;i<=m;i++){
		scanf("%d %d %d",&k,&x,&y);
		if(k==1 || k==2) scanf("%lld",&op);
		if(k==1) mult(1,x,y,op);
		else if(k==2) add(1,x,y,op);
		else printf("%lld\n",get_tot(1,x,y));
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值