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 a≥b,则 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 a≥b时 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(0≤p<b),则当 a ≤ b a\leq b a≤b时 k ≥ 1 k\geq 1 k≥1, k b ≥ b > p kb\geq b>p kb≥b>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;
}