题意:
分析:区间整除和区间开根号有点像。有人也叫势能线段树,由于每次区间整除之后区间的数都至少要减少到原来的一半,logai次之后区间的数就变为 1了。加上区间加操作,操作的次数也不会多太多。维护一个区间 max、min和sum,当max-max/k=min-min/k时,就相当于区间每个数都减去max-max/k。(最大值整除后作差与最小值整除后作差的差值相同,则可以把差值打标记,即由除变成区间减的标记) 。
势能分析线段树是这样的,对于某一些操作,不打标记,暴力更改但操作很少次以后就不会改变结果了(最常见的就是区间开根号和区间整除),维护一些东西来表示这个区间是否会改变。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lc (o<<1)
#define rc (o<<1|1)
#define mid (l+r>>1)
const int N=1e5+10;
ll a[N],sum[N<<2],mx[N<<2],mn[N<<2],lazy[N<<2];
int n,q;//人数,政令+询问个数
ll divide(ll x,ll y){
return floor((double)x/y);
}
void push_up(int o){
sum[o]=sum[lc]+sum[rc];
mx[o]=max(mx[lc],mx[rc]);
mn[o]=min(mn[lc],mn[rc]);
}
void build(int o,int l,int r){
if(l==r){
mx[o]=mn[o]=sum[o]=a[l];
lazy[o]=0;
return;
}
build(lc,l,mid);build(rc,mid+1,r);
push_up(o);
}
void push_down(int o,int l,int r){
if(!lazy[o])return;
lazy[lc]+=lazy[o];
sum[lc]+=(mid-l+1)*lazy[o];
mx[lc]+=lazy[o];
mn[lc]+=lazy[o];
lazy[rc]+=lazy[o];
sum[rc]+=(r-mid)*lazy[o];
mx[rc]+=lazy[o];
mn[rc]+=lazy[o];
lazy[o]=0;
}
void add(int o,int l,int r,int nl,int nr,ll v){
if(l<r)push_down(o,l,r);
if(nl<=l&&r<=nr){
sum[o]+=(ll)(r-l+1)*v;
lazy[o]+=v;
mn[o]+=v;
mx[o]+=v;
return;
}
if(nl<=mid)add(lc,l,mid,nl,nr,v);
if(nr>mid)add(rc,mid+1,r,nl,nr,v);
push_up(o);
}
void div(int o,int l,int r,int nl,int nr,ll v){
if(l<r)push_down(o,l,r);
if(nl<=l&&r<=nr){
ll m1=divide(mx[o],v);
ll m2=divide(mn[o],v);
if(mx[o]-m1==mn[o]-m2){
ll k=mx[o]-m1;
sum[o]-=k*(r-l+1);//写成了k*(r-l+1)*v惊了
mx[o]-=k;
mn[o]-=k;
lazy[o]-=k;
return;
}
}
if(nl<=mid)div(lc,l,mid,nl,nr,v);
if(nr>mid)div(rc,mid+1,r,nl,nr,v);
push_up(o);
}
ll query_min(int o,int l,int r,int nl,int nr){
if(nl<=l&&r<=nr)
return mn[o];
push_down(o,l,r);
if(nr<=mid)return query_min(lc,l,mid,nl,nr);
else if(nl>mid)return query_min(rc,mid+1,r,nl,nr);
else return min(query_min(lc,l,mid,nl,mid),query_min(rc,mid+1,r,mid+1,nr));
}
ll query_sum(int o,int l,int r,int nl,int nr){
if(nl<=l&&r<=nr)
return sum[o];
push_down(o,l,r);
if(nr<=mid)return query_sum(lc,l,mid,nl,nr);
else if(nl>mid)return query_sum(rc,mid+1,r,nl,nr);
else return query_sum(lc,l,mid,nl,mid)+query_sum(rc,mid+1,r,mid+1,nr);
}
int main(){
scanf("%d%d",&n,&q);
int op,x,y;
ll z;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,1,n);
for(int i=1;i<=q;i++){
scanf("%d",&op);
if(op==1) scanf("%d%d%lld",&x,&y,&z),add(1,1,n,x+1,y+1,z);
else if(op==2) scanf("%d%d%lld",&x,&y,&z),div(1,1,n,x+1,y+1,z);
else if(op==3) scanf("%d%d",&x,&y),printf("%lld\n",query_min(1,1,n,x+1,y+1));
else if(op==4) scanf("%d%d",&x,&y),printf("%lld\n",query_sum(1,1,n,x+1,y+1));
}
return 0;
}