题目大意
给你一个长度为 n n n的序列 a a a,有 q q q次操作,操作有四种:
1,l,r,c
:对于 i ∈ [ l , r ] i\in[l,r] i∈[l,r], a i = a i + c a_i=a_i+c ai=ai+c2,l,r,d
:对于 i ∈ [ l , r ] i\in[l,r] i∈[l,r], a i = ⌊ a i d ⌋ a_i=\lfloor\dfrac{a_i}{d}\rfloor ai=⌊dai⌋3,l,r
:求 min i = l r a i \min\limits_{i=l}^ra_i i=lminrai4,l,r
:求 ∑ i = l r a i \sum\limits_{i=l}^ra_i i=l∑rai
题解
这道题涉及到区间修改和区间查询,我们可以用线段树来维护序列 a a a。
对于操作1,直接区间修改即可。对于操作3和操作4,直接区间查询即可。
对于操作2,我们需要把区间除法转化为区间减法。对每一个区间维护一个最小值 m n mn mn和最大值 m x mx mx,如果 m n − ⌊ m n d ⌋ = m x − ⌊ m x d ⌋ mn-\lfloor\frac{mn}{d}\rfloor=mx-\lfloor\frac{mx}{d}\rfloor mn−⌊dmn⌋=mx−⌊dmx⌋,则说明这一段中每个数减去这个数除以 d d d后的值的差都相等,那么将区间中的每一个数都减去 m n − ⌊ m n d ⌋ mn-\lfloor\frac{mn}{d}\rfloor mn−⌊dmn⌋后就能得到这个数除以 d d d后的值。
那为什么这样做不会TLE呢?
首先,如果只有除法,则对于 a i a_i ai,最多经过 log a i \log a_i logai次除法就会变成 1 1 1或 0 0 0,然后就可以进行上面的操作了,时间复杂度可记为 O ( log C ) O(\log C) O(logC),其中 C = 1 0 9 + 1 0 4 ⋅ q C=10^9+10^4\cdot q C=109+104⋅q,即 a i a_i ai可能达到的最大值。
但因为还有加法操作,每次操作最多会使一个可以将区间除法用区间减法来做的区间分成 log n \log n logn块,这些块都将要经过 log C \log C logC次除法才能合并,也就是将要进行 O ( log n log C ) O(\log n\log C) O(lognlogC)的时间复杂度的除法,则 q q q次操作的时间复杂度为 O ( q log n log C ) O(q\log n\log C) O(qlognlogC)。一开始最多有 n n n个这样的区间,最多会导致的除法分时间复杂度为 O ( n log C ) O(n\log C) O(nlogC)。直接的区间修改和区间查询的时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。所以总时间复杂度为 O ( n log C + q log n log C ) O(n\log C+q\log n\log C) O(nlogC+qlognlogC)。
code
#include<bits/stdc++.h>
#define lc k<<1
#define rc k<<1|1
using namespace std;
int n,q,a[500005];
long long ans,v[500005],mn[500005],mx[500005],ly[500005];
void pt(int k){
v[k]=v[lc]+v[rc];
mn[k]=min(mn[lc],mn[rc]);
mx[k]=max(mx[lc],mx[rc]);
}
void build(int k,int l,int r){
if(l==r){
v[k]=mn[k]=mx[k]=a[l];
return;
}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pt(k);
}
void add(int k,int len,int c){
v[k]+=len*c;
mn[k]+=c;
mx[k]+=c;
ly[k]+=c;
}
void down(int k,int l,int r){
int mid=l+r>>1;
add(lc,mid-l+1,ly[k]);
add(rc,r-mid,ly[k]);
ly[k]=0;
}
void ch(int k,int l,int r,int x,int y,int c){
if(l>=x&&r<=y){
add(k,r-l+1,c);
return;
}
if(ly[k]) down(k,l,r);
int mid=l+r>>1;
if(x<=mid) ch(lc,l,mid,x,y,c);
if(y>mid) ch(rc,mid+1,r,x,y,c);
pt(k);
}
void dv(int k,int l,int r,int x,int y,int c){
if(l>=x&&r<=y){
int v1=floor((double)mn[k]/c),v2=floor((double)mx[k]/c);
if(mn[k]-v1==mx[k]-v2){
add(k,r-l+1,-mn[k]+v1);
return;
}
}
if(ly[k]) down(k,l,r);
int mid=l+r>>1;
if(x<=mid) dv(lc,l,mid,x,y,c);
if(y>mid) dv(rc,mid+1,r,x,y,c);
pt(k);
}
void gtmin(int k,int l,int r,int x,int y){
if(l>=x&&r<=y){
ans=min(ans,mn[k]);
return;
}
if(ly[k]) down(k,l,r);
int mid=l+r>>1;
if(x<=mid) gtmin(lc,l,mid,x,y);
if(y>mid) gtmin(rc,mid+1,r,x,y);
}
void gtsum(int k,int l,int r,int x,int y){
if(l>=x&&r<=y){
ans+=v[k];
return;
}
if(ly[k]) down(k,l,r);
int mid=l+r>>1;
if(x<=mid) gtsum(lc,l,mid,x,y);
if(y>mid) gtsum(rc,mid+1,r,x,y);
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build(1,1,n);
int tp,l,r,c;
while(q--){
scanf("%d",&tp);
if(tp==1){
scanf("%d%d%d",&l,&r,&c);++l;++r;
ch(1,1,n,l,r,c);
}
else if(tp==2){
scanf("%d%d%d",&l,&r,&c);
++l;++r;
dv(1,1,n,l,r,c);
}
else if(tp==3){
scanf("%d%d",&l,&r);++l;++r;
ans=1e15;
gtmin(1,1,n,l,r);
printf("%lld\n",ans);
}
else{
scanf("%d%d",&l,&r);++l;++r;
ans=0;
gtsum(1,1,n,l,r);
printf("%lld\n",ans);
}
}
return 0;
}