(分块)LOJ#6281. 数列分块入门 5

传送门:LOJ#6281. 数列分块入门 5

题意:给出一个长为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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值