Codeforces 48D \color{green}{\text{Codeforces\ \ \ 48D}} Codeforces 48D
【 题 目 大 意 】 : \color{blue}{【题目大意】:} 【题目大意】: 给定一个序列 A A A,有以下三种操作:
- 询问区间 [ l , r ] [l,r] [l,r]的总和
- 区间 [ l , r ] [l,r] [l,r]内的所有数同时对 x x x取模
- 把第 l l l个数修改成 x x x
记序列长为 N N N,操作个数为 M M M,则 1 ≤ N , M ≤ 1 × 1 0 5 1 \leq N,M \leq 1 \times 10^5 1≤N,M≤1×105。
【 思 路 】 : \color{blue}{【思路】:} 【思路】: 让我们先来看看取模的性质: a m o d b ≤ ⌊ a 2 ⌋ a\mod b \leq \lfloor \dfrac{a}{2} \rfloor amodb≤⌊2a⌋。
我们现在来尝试证明它:
- 当 b ≤ ⌊ a 2 ⌋ b \leq \lfloor \dfrac{a}{2} \rfloor b≤⌊2a⌋时,因为 a m o d b ≤ b a\mod b \leq b amodb≤b,所以肯定有 a m o d b ≤ ⌊ a 2 ⌋ a\mod b \leq \lfloor \dfrac{a}{2} \rfloor amodb≤⌊2a⌋。
- 当 b > ⌊ a 2 ⌋ b > \lfloor \dfrac{a}{2} \rfloor b>⌊2a⌋时,因为 a m o d b = a − b × ⌊ a b ⌋ a \mod b=a- b \times \lfloor \dfrac{a}{b} \rfloor amodb=a−b×⌊ba⌋,所以也有 a m o d b ≤ ⌊ a 2 ⌋ a\mod b \leq \lfloor \dfrac{a}{2} \rfloor amodb≤⌊2a⌋。
有了这个限制,我们发现用时最长的取模操作会很快就会失去其执行的意义,即如果一个区间对 m o d mod mod取模但区间内所有的数都小于 m o d mod mod,这个取模操作是不必执行的。
既然如此,我们就随便维护区间的最大值和总和,用最普通的方法也不会TLE
,只要觉得当区间最大值都小于
m
o
d
mod
mod时,不需进行取模操作(因为所有的数字都非负)。
【 代 码 】 : \color{blue}{【代码】:} 【代码】:
const int N=1e5+100;
typedef long long ll;
int n,m,a[N],opt,l,r;
struct Segment_tree{
ll Max[N<<2],sum[N<<2];
void pushup(int o){
Max[o]=max(Max[o<<1],Max[o<<1|1]);
sum[o]=sum[o<<1]+sum[o<<1|1];
}
void build(int o,int l,int r){
if (l==r){
Max[o]=sum[o]=a[l];
return;
}
register int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);return;
}
void updata1(int o,int l,int r,int p,int q,ll mod){
if (Max[o]<mod) return;
if (l==r){
Max[o]=sum[o]=sum[o]%mod;
return;
}
register int mid=(l+r)>>1;
if (p<=mid) updata1(o<<1,l,mid,p,q,mod);
if (q>mid) updata1(o<<1|1,mid+1,r,p,q,mod);
pushup(o);return;
}
void updata2(int o,int l,int r,int p,ll v){
if (l==r){
Max[o]=sum[o]=v;
return;
}
register int mid=(l+r)>>1;
if (p<=mid) updata2(o<<1,l,mid,p,v);
if (p>mid) updata2(o<<1|1,mid+1,r,p,v);
pushup(o);return;
}
ll query(int o,int l,int r,int p,int q){
if (l>q||r<p) return 0ll;
if (p<=l&&r<=q) return sum[o];
register int mid=(l+r)>>1;
register ll answer=0ll;
answer+=query(o<<1,l,mid,p,q);
answer+=query(o<<1|1,mid+1,r,p,q);
return answer;
}
}Seg;
int main(){
// freopen("t1.in","r",stdin);
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
Seg.build(1,1,n);
for(int i=1;i<=m;i++){
opt=read();l=read();r=read();
if (opt==1) printf("%lld\n",Seg.query(1,1,n,l,r));
else if (opt==2) Seg.updata1(1,1,n,l,r,read());
else Seg.updata2(1,1,n,l,r);
}
return 0;
}
【 小 结 】 : \color{blue}{【小结】:} 【小结】: 我们要善于发现题目中操作的特殊性质,并且好好对它进行利用,这样,才能很好地把题做好!!!