题目大意
维护一个序列,支持三种操作
1、区间加法
2、区间开方下取整
3、查询区间和
线段树
有一道bzoj上的名为上帝造题的七分钟的题目没有加法操作。
有了我们怎么做呢?
假设一个区间所有数都相同,显然对它开方就是一个赋值标记嘛。
存在不同呢?
对于两个数a和b设a大于b。
原本它们的差是a-b
开方后差变成了
a√−b√=a−ba√+b√
我们又知道
a√−b√<a√+b√
相当于每次把差变成比原来的开方还小了。
那就和bzoj那道差不多了。
对一个数不停开方一直开方到开方不会再产生变化只需要很少的步数。
因此对于一个区间最大值不等于最小值的情况,可以考虑暴力递归左右。
不过可以注意到两个数的差开方可能会开方到1然后也不再变化了。
因此区间的最大值和最小值只相差1时也需要讨论开方是否会使得它们的差产生变化,不产生变化的话可以直接打加法标记。
这样就可以做了。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
ll mx[maxn*4],mi[maxn*4],sum[maxn*4],ad[maxn*4],st[maxn*4];
int a[maxn*4];
bool bz[maxn*4];
int i,j,k,l,r,x,t,n,m;
ll ans;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void build(int p,int l,int r){
if (l==r){
sum[p]=mx[p]=mi[p]=a[l];
return;
}
int mid=(l+r)/2;
build(p*2,l,mid);build(p*2+1,mid+1,r);
sum[p]=sum[p*2]+sum[p*2+1];
mx[p]=max(mx[p*2],mx[p*2+1]);
mi[p]=min(mi[p*2],mi[p*2+1]);
}
void markst(int p,int l,int r,ll v){
ad[p]=0;
mx[p]=mi[p]=v;
sum[p]=(ll)(r-l+1)*v;
bz[p]=1;
st[p]=v;
}
void markad(int p,int l,int r,ll v){
ad[p]+=v;
mx[p]+=v;
mi[p]+=v;
sum[p]+=(ll)(r-l+1)*v;
}
void down(int p,int l,int r){
int mid=(l+r)/2;
if (bz[p]){
markst(p*2,l,mid,st[p]);
markst(p*2+1,mid+1,r,st[p]);
bz[p]=0;
}
if (ad[p]){
markad(p*2,l,mid,ad[p]);
markad(p*2+1,mid+1,r,ad[p]);
ad[p]=0;
}
}
void change(int p,int l,int r,int a,int b,int v){
if (l==a&&r==b){
markad(p,l,r,v);
return;
}
int mid=(l+r)/2;
down(p,l,r);
if (b<=mid) change(p*2,l,mid,a,b,v);
else if (a>mid) change(p*2+1,mid+1,r,a,b,v);
else{
change(p*2,l,mid,a,mid,v);
change(p*2+1,mid+1,r,mid+1,b,v);
}
sum[p]=sum[p*2]+sum[p*2+1];
mx[p]=max(mx[p*2],mx[p*2+1]);
mi[p]=min(mi[p*2],mi[p*2+1]);
}
void kf(int p,int l,int r,int a,int b){
if (l==a&&r==b){
if (mx[p]==mi[p]){
markst(p,l,r,int(sqrt(mx[p])));
return;
}
else if (mx[p]-mi[p]==1){
if (int(sqrt(mi[p]))==int(sqrt(mx[p]))) markst(p,l,r,int(sqrt(mx[p])));
else markad(p,l,r,int(sqrt(mi[p]))-mi[p]);
return;
}
down(p,l,r);
int mid=(l+r)/2;
kf(p*2,l,mid,a,mid);kf(p*2+1,mid+1,r,mid+1,b);
sum[p]=sum[p*2]+sum[p*2+1];
mx[p]=max(mx[p*2],mx[p*2+1]);
mi[p]=min(mi[p*2],mi[p*2+1]);
return;
}
down(p,l,r);
int mid=(l+r)/2;
if (b<=mid) kf(p*2,l,mid,a,b);
else if (a>mid) kf(p*2+1,mid+1,r,a,b);
else{
kf(p*2,l,mid,a,mid);
kf(p*2+1,mid+1,r,mid+1,b);
}
sum[p]=sum[p*2]+sum[p*2+1];
mx[p]=max(mx[p*2],mx[p*2+1]);
mi[p]=min(mi[p*2],mi[p*2+1]);
}
ll query(int p,int l,int r,int a,int b){
if (l==a&&r==b) return sum[p];
down(p,l,r);
int mid=(l+r)/2;
if (b<=mid) return query(p*2,l,mid,a,b);
else if (a>mid) return query(p*2+1,mid+1,r,a,b);
else return query(p*2,l,mid,a,mid)+query(p*2+1,mid+1,r,mid+1,b);
}
int main(){
n=read();m=read();
fo(i,1,n) a[i]=read();
build(1,1,n);
while (m--){
t=read();
if (t==1){
l=read();r=read();x=read();
change(1,1,n,l,r,x);
}
else if (t==2){
l=read();r=read();
kf(1,1,n,l,r);
}
else{
l=read();r=read();
ans=query(1,1,n,l,r);
printf("%lld\n",ans);
}
}
}