CodeForces 438D The Child and Sequence(线段树区间取模)

文章介绍了CodeForces上的一道题目TheChildandSequence,主要涉及数据结构线段树的使用,特别是处理区间内数字对给定值取模的操作。题解中解释了如何利用线段树来支持单点修改、区间查询以及区间取模等操作,并提供了代码实现。

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

CodeForces 438D The Child and Sequence

洛谷 The Child and Sequence

题目大意

给出一个长度为 n n n的非负整数序列 a a a,需要支持以下操作:

  • 给出 l , r l,r l,r,求 l , r l,r l,r之间的数的和
  • 给出 l , r , x l,r,x l,r,x,将 l , r l,r l,r之间的每个 a i a_i ai都对 x x x取模
  • 给出 k , v k,v k,v,将 a k a_k ak的值改为 v v v

题解

如果只有 1 , 3 1,3 1,3操作,那么这道题就是一道满足单点修改和区间查询的线段树模板。下面,我们考虑如何处理区间取模操作。

a , b a,b a,b都为正整数,则

  • a < b a<b a<b,则 a % b = a a\%b=a a%b=a
  • a ≥ b a\geq b ab,则 a % b < a / 2 a\%b<a/2 a%b<a/2

a < b a<b a<b a % b = a a\%b=a a%b=a很显然,那为什么 a ≥ b a\geq b ab a % b < a / 2 a\%b<a/2 a%b<a/2呢?

a = k b + p ( 0 ≤ p < b ) a=kb+p(0\leq p <b) a=kb+p(0p<b),则当 a ≤ b a\leq b ab k ≥ 1 k\geq 1 k1 k b ≥ b > p kb\geq b>p kbb>p,可得 k b + p > 2 p kb+p> 2p kb+p>2p,即 a > 2 p a>2p a>2p,可得 p < a / 2 p<a/2 p<a/2

所以,对于一个数 x x x,在不修改的情况下,只会被模 log ⁡ x \log x logx次,最多只会修改 m m m次,所以总时间复杂度为 O ( ( n + m ) log ⁡ w ) O((n+m)\log w) O((n+m)logw),其中 w w w为最大的 a i a_i ai值。

注意在线段树中不仅要维护区间和,还要维护区间最大值。对于操作 2 2 2,如果一个区间的最大值小于当前的模数,则这个区间就不用修改了。对于需要模的 a i a_i ai,我们暴力地将每一个值都模一遍。根据上面的分析,这样处理不会TLE。

code

#include<bits/stdc++.h>
#define lc k<<1
#define rc k<<1|1
using namespace std;
int n,m;
long long ans,tr[400005],mx[400005];
void  pushup(int k){
	tr[k]=tr[lc]+tr[rc];
	mx[k]=max(mx[lc],mx[rc]);
}
void build(int k,int l,int r){
	if(l==r){
		scanf("%lld",&tr[k]);
		mx[k]=tr[k];
		return;
	}
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(k);
}
void pt(int k,int l,int r,int x,int y,int t){
	if(mx[k]<t) return;//判断区间最大值是否大于等于当前模数
	if(l==r){//将每一个值都模一遍
		tr[k]%=t;
		mx[k]=tr[k];
		return;
	}
	int mid=l+r>>1;
	if(x<=mid) pt(lc,l,mid,x,y,t);
	if(y>mid) pt(rc,mid+1,r,x,y,t);
	pushup(k);
}
void ch(int k,int l,int r,int x,int t){
	if(l==r){
		mx[k]=tr[k]=t;
		return;
	}
	int mid=l+r>>1;
	if(x<=mid) ch(lc,l,mid,x,t);
	else ch(rc,mid+1,r,x,t);
	pushup(k);
}
void find(int k,int l,int r,int x,int y){
	if(l>=x&&r<=y){
		ans+=tr[k];
		return;
	}
	int mid=l+r>>1;
	if(x<=mid) find(lc,l,mid,x,y);
	if(y>mid) find(rc,mid+1,r,x,y);
}
int main()
{
	scanf("%d%d",&n,&m);
	build(1,1,n);
	for(int i=1,tp,l,r,k,x;i<=m;i++){
		scanf("%d",&tp);
		if(tp==1){
			scanf("%d%d",&l,&r);
			ans=0;
			find(1,1,n,l,r);
			printf("%lld\n",ans);
		}
		else if(tp==2){
			scanf("%d%d%d",&l,&r,&x);
			pt(1,1,n,l,r,x);
		}
		else{
			scanf("%d%d",&k,&x);
			ch(1,1,n,k,x);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值