题目
思路
如果区间内数相同,可以直接开根
但这样显然太慢了
我们考虑把开根变成减法,也就是开根后相差的相同的同时做减法
加上这一优化后我们发现每次开根最大最小值之差是每次除以O(sqrt(m))的
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+77;
int n,m,o,x,y,z,a[N];ll sm[N*4],lz[N*4],mx[N*4],mn[N*4];
void up(int x){sm[x]=sm[x*2]+sm[x*2+1];mx[x]=max(mx[x*2],mx[x*2+1]);mn[x]=min(mn[x*2],mn[x*2+1]);}
void put(int x,int l,int r,ll w){sm[x]+=w*(r-l+1);mx[x]+=w;mn[x]+=w;lz[x]+=w;}
void dn(int x,int l,int r){int mid=(l+r)>>1;put(x*2,l,mid,lz[x]);put(x*2+1,mid+1,r,lz[x]);lz[x]=0;}
void bud(int x,int l,int r)
{
if(l==r){mx[x]=sm[x]=mn[x]=a[l];return;}int mid=(l+r)>>1;
bud(x*2,l,mid);bud(x*2+1,mid+1,r);up(x);
}
void update(int x,int l,int r,int b,int e,ll v)
{
if(b<=l&&r<=e){put(x,l,r,v);return;}int mid=(l+r)>>1;dn(x,l,r);
if(b<=mid) update(x*2,l,mid,b,e,v);if(e>mid) update(x*2+1,mid+1,r,b,e,v);up(x);
}
ll query(int x,int l,int r,int b,int e)
{
if(b<=l&&r<=e) return sm[x];int mid=(l+r)>>1;dn(x,l,r);
return (b<=mid?query(x*2,l,mid,b,e):0)+(e>mid?query(x*2+1,mid+1,r,b,e):0);
}
void modify(int x,int l,int r,int b,int e)
{
if(b<=l&&r<=e){ll y=sqrt(mx[x]),z=sqrt(mn[x]);if(mx[x]-y==mn[x]-z){put(x,l,r,y-mx[x]);return;}}
int mid=(l+r)>>1;dn(x,l,r);if(b<=mid) modify(x*2,l,mid,b,e);if(e>mid) modify(x*2+1,mid+1,r,b,e);up(x);
}
int main()
{
scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&a[i]);bud(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&o,&x,&y);
if(o==1) scanf("%d",&z);
if(o==1) update(1,1,n,x,y,z);
if(o==2) modify(1,1,n,x,y);
if(o==3) printf("%lld\n",query(1,1,n,x,y));
}
}