loj 6029
势能线段树。定义势能为 \(\log(\max -\min)\),那么区间加操作只会影响端点处增加 \(\log\),区间除法每暴力遍历一个子树就给势能至少减一。因此时间复杂度 \(\log^2\)。
“区间除法每暴力遍历一个子树就给势能至少减一”这句话虽然大部分情况是正确的,但是例如 -1 0 -1 0 ... 之类的不能有减少,也就是 \(\max-\min=1\) 的时候有可能按照 min==max 判断会 TLE。因此判断方式改变为 min-min/d=max-max/d 即可。
注意下取整中负数的情况。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5+5;
ll n,q,a[N];
ll s[N<<2],mn[N<<2],tg[N<<2],mx[N<<2];
void F(int k,ll len,ll v){
s[k]+=len*v;
mn[k]+=v;
mx[k]+=v;
tg[k]+=v;
}
void pu(int k){
s[k]=s[k<<1]+s[k<<1|1];
mn[k]=min(mn[k<<1],mn[k<<1|1]);
mx[k]=max(mx[k<<1],mx[k<<1|1]);
}
void pd(int k,int l,int r){
if (tg[k]){
int mid=l+r>>1;
F(k<<1,mid-l+1,tg[k]);
F(k<<1|1,r-mid,tg[k]);
tg[k]=0;
}
}
void upda(int k,int l,int r,int ql,int qr,ll d){
if (ql<=l && r<=qr){
F(k,r-l+1,d);
return;
}
if (r<ql || l>qr) return;
int mid=l+r>>1;
pd(k,l,r);
upda(k<<1,l,mid,ql,qr,d);
upda(k<<1|1,mid+1,r,ql,qr,d);
pu(k);
}
ll dv(ll x,ll d){
if (x>=0) return x/d;
return -(-x+d-1)/d;
}
void updd(int k,int l,int r,int ql,int qr,ll d){
if (r<ql || l>qr) return;
if (ql<=l && r<=qr && mn[k]-dv(mn[k],d)==mx[k]-dv(mx[k],d)){
F(k,r-l+1,-mn[k]+dv(mn[k],d));
return;
}
int mid=l+r>>1;
pd(k,l,r);
updd(k<<1,l,mid,ql,qr,d);
updd(k<<1|1,mid+1,r,ql,qr,d);
pu(k);
}
ll as,amn;
void qy(int k,int l,int r,int ql,int qr){
if (ql<=l && r<=qr){
as+=s[k];
amn=min(amn,mn[k]);
return;
}
if (r<ql || l>qr) return;
int mid=l+r>>1;
pd(k,l,r);
qy(k<<1,l,mid,ql,qr);
qy(k<<1|1,mid+1,r,ql,qr);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>q;
for (int i=1; i<=n; i++){
cin>>a[i];
upda(1,1,n,i,i,a[i]);
}
while (q--){
ll o,l,r,v;
cin>>o>>l>>r;
l++,r++;
if (o<=2) cin>>v;
if (o==1) upda(1,1,n,l,r,v);
else if (o==2) updd(1,1,n,l,r,v);
else{
as=0,amn=1e18;
qy(1,1,n,l,r);
if (o==4) cout<<as<<"\n";
else cout<<amn<<"\n";
}
}
return 0;
}
浙公网安备 33010602011771号