题意:维护一个数列,支持区间加,区间开根下取整,区间求和
最暴力的想法当然是用线段树维护,对于开根,如果当前区间内的数全相同,那么打一个覆盖标记,否则递归处理
这样当然是会被卡掉的:对于数列$\{2,3,2,3,\cdots\}$,重复$+6$和开根操作,这样就可以把上面那种暴力做法卡掉了
但其实这种暴力已经离正解很近了,考虑维护区间极差,如果递归到当前区间的极差$\geq2$,那么按暴力的方法做,否则分情况打一个区间减标记或者覆盖标记
为什么这样是对的?不妨设区间最小值为$mn$,最大值为$mx$,且$mx-mn\geq2$,那么$\left\lfloor\sqrt{mx+d}\right\rfloor-\left\lfloor\sqrt{mn+d}\right\rfloor=\left\lfloor\sqrt{mn+d+(mx-mn)}\right\rfloor-\left\lfloor\sqrt{mn+d}\right\rfloor$
考虑函数$f(x)=\left\lfloor\sqrt{x}\right\rfloor$
容易发现对任意的$d\geq2$,$f(x+d)-f(x)\lt d$
有了这个结论,上面的式子告诉我们一段极差$\geq2$的数开根下取整后极差会变小,而且开根本来就让数字减小得很快,所以对极差$\geq2$的区间,暴力也可以保证复杂度
那个用来卡暴力的数据告诉我们极差$=1$的情况要特别处理,如果$\sqrt{mx}=\sqrt{mn}$,打上覆盖标记,如果$\sqrt{mx}=\sqrt{mn}+1$,打上区间减标记
#include<stdio.h>
#include<math.h>
#define ll long long
#define inf 9223372036854775807ll
int p[100010];
ll s[400010],d1[400010],d2[400010],mx[400010],mn[400010];
//v'=d1*v+d2
void gao(int x,ll len,ll f1,ll f2){
d1[x]*=f1;
d2[x]=d2[x]*f1+f2;
s[x]=s[x]*f1+len*f2;
mx[x]=mx[x]*f1+f2;
mn[x]=mn[x]*f1+f2;
}
void pushdown(int x,int l,int r){
int mid=(l+r)>>1;
if(d1[x]!=1||d2[x]){
gao(x<<1,mid-l+1,d1[x],d2[x]);
gao(x<<1|1,r-mid,d1[x],d2[x]);
d1[x]=1;
d2[x]=0;
}
}
ll min(ll a,ll b){return a<b?a:b;}
ll max(ll a,ll b){return a>b?a:b;}
void pushup(int x){
s[x]=s[x<<1]+s[x<<1|1];
mx[x]=max(mx[x<<1],mx[x<<1|1]);
mn[x]=min(mn[x<<1],mn[x<<1|1]);
}
void modify(int L,int R,ll f1,ll f2,int l,int r,int x){
if(L<=l&&r<=R)return gao(x,r-l+1,f1,f2);
pushdown(x,l,r);
int mid=(l+r)>>1;
if(L<=mid)modify(L,R,f1,f2,l,mid,x<<1);
if(mid<R)modify(L,R,f1,f2,mid+1,r,x<<1|1);
pushup(x);
}
ll querysum(int L,int R,int l,int r,int x){
if(L<=l&&r<=R)return s[x];
pushdown(x,l,r);
int mid=(l+r)>>1;
ll ans=0;
if(L<=mid)ans+=querysum(L,R,l,mid,x<<1);
if(mid<R)ans+=querysum(L,R,mid+1,r,x<<1|1);
return ans;
}
void sqrt(int L,int R,int l,int r,int x){
if(L<=l&&r<=R&&mx[x]-mn[x]<=1){
ll sqx,sqn;
sqx=sqrt(mx[x]);
sqn=sqrt(mn[x]);
if(sqx==sqn)
gao(x,r-l+1,0,sqx);
else
gao(x,r-l+1,1,sqx-mx[x]);
return;
}
pushdown(x,l,r);
int mid=(l+r)>>1;
if(L<=mid)sqrt(L,R,l,mid,x<<1);
if(mid<R)sqrt(L,R,mid+1,r,x<<1|1);
pushup(x);
}
void build(int l,int r,int x){
d1[x]=1;
d2[x]=0;
if(l==r){
s[x]=mx[x]=mn[x]=p[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,x<<1);
build(mid+1,r,x<<1|1);
pushup(x);
}
int main(){
int t,n,m,i,op,l,r,x;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)scanf("%d",p+i);
build(1,n,1);
while(m--){
scanf("%d%d%d",&op,&l,&r);
if(op==1){
scanf("%d",&x);
modify(l,r,1,x,1,n,1);
}
if(op==2)sqrt(l,r,1,n,1);
if(op==3)printf("%lld\n",querysum(l,r,1,n,1));
}
}
}