题意:给出一个长为n的数列,以及n个操作,操作涉及区间开方,区间求和。
对于区间开方,对于整个块似乎不能进行操作。所以得对整个块进行暴力修改。
这个算法优化的关键点就是无论哪个数经过多次开方都会变成0或1,这样的话再怎么开方,块内的这些数都不会怎么改变.我们可以对块内元素经过修改是否都变为0或1进行标记,如果整个块都变为0或1,那么再对块进行修改时就可忽略。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=5e4+10;
typedef long long ll;
ll a[maxn],n,block,pos[maxn],sum[maxn];
bool flag[maxn];
void solve_sqrt(ll x){
if(flag[x]) return ;
flag[x]=true;
sum[x]=0;
for(int i=(x-1)*block+1;i<=x*block;i++){
a[i]=sqrt(a[i]);
sum[x]+=a[i];
if(a[i]>1) flag[x]=false;
}
}
void add(ll l,ll r){
for(ll i=l;i<=min(pos[l]*block,r);i++){
if(flag[pos[l]]) break;
sum[pos[i]]-=a[i];
a[i]=sqrt(a[i]);
sum[pos[i]]+=a[i];
}
if(pos[l]!=pos[r]){
for(ll i=(pos[r]-1)*block+1;i<=r;i++){
if(flag[pos[r]]) break;
sum[pos[i]]-=a[i];
a[i]=sqrt(a[i]);
sum[pos[i]]+=a[i];
}
}
for(ll i=pos[l]+1;i<pos[r];i++){
solve_sqrt(i);
}
}
ll query(ll l,ll r){
ll ans=0;
for(ll i=l;i<=min(pos[l]*block,r);i++){
ans+=a[i];
}
if(pos[l]!=pos[r]){
for(ll i=(pos[r]-1)*block+1;i<=r;i++){
ans+=a[i];
}
}
for(ll i=pos[l]+1;i<pos[r];i++){
ans+=sum[i];
}
return ans;
}
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
block=sqrt(n);
for(ll i=1;i<=n;i++){
pos[i]=(i-1)/block+1;
sum[pos[i]]+=a[i];
}
ll opt,l,r,c;
for(int i=0;i<n;i++){
scanf("%lld%lld%lld%lld",&opt,&l,&r,&c);
if(opt==0) add(l,r);
else printf("%lld\n",query(l,r));
}
return 0;
}