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;
}
posted @ 2025-04-09 09:38  SFlyer  阅读(18)  评论(0)    收藏  举报