当前位置: 首页 > news >正文

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

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;
}

相关文章:

  • Linux——系统管理篇
  • 【Java】面向对象笔记(中)(二)
  • 注册中心和负载均衡(黑马SpringCloud笔记)
  • DSPE-PEG-Silane,磷脂-聚乙二醇-硅烷,简介。可用于修饰玻璃、二氧化硅颗粒和许多其他材料表面
  • Acwing---1229.日期问题
  • 01.数据的存储.练习题
  • 1.6 阿达马积
  • verilog学习笔记- 14)静态数码管显示实验
  • Web测试的各个测试点
  • 好家伙,这几个隐藏功能,太香了
  • Android View类
  • 算法训练营 day20 二叉树 最大二叉树 合并二叉树 二叉搜索树中的搜索 验证二叉树
  • 系分 - 案例分析 - 架构设计(Web架构)
  • Allegro如何更改钻孔孔符以及大小操作指导
  • Java基础篇01-运算符的使用
  • 2022年度总结 - 明月醉窗台
  • 个人总结:Mysql知识图谱
  • SSM 05 SpringBoot yaml mybatisplus
  • 十五天学会Autodesk Inventor,看完这一系列就够了(七),工程图纸
  • 快速理解机器学习、深度学习与自然语言处理