上帝造题的七分钟2 / 花神游历各国(分块)

好吧,楼下有分块的解法,那么我就再阐述一遍好了

本以为暴力分块为(TLE)的,结果发现吊打线段树,用奇技淫巧的卡常技术卡到第一页

评测记录

其实这道题就是数列分块入门(5)嘛,发现一个数只能被不超过(6)次开方,那么暴力修改,用一个标记看看整块是否有大于(1)的数

那么我就献上一个未卡常的分块解法

(Code Below:)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100000+10;
ll n,m,a[maxn],sum[330],v[330],pos[maxn],blo;

void change(ll x){
    if(!v[x]){
        v[x]=1;sum[x]=0;
        for(ll i=(x-1)*blo+1;i<=x*blo;i++){
            a[i]=sqrt(a[i]);
            sum[x]+=a[i];
            if(a[i]>1) v[x]=0;
        }
    }
}

void update(ll l,ll r){
    if(!v[pos[l]]){
        for(ll i=l;i<=min(pos[l]*blo,r);i++){
            sum[pos[i]]-=a[i];
            a[i]=sqrt(a[i]);
            sum[pos[i]]+=a[i];
        }
        v[pos[l]]=1;
        for(ll i=(pos[l]-1)*blo+1;i<=min(pos[l]*blo,n);i++){
            if(a[i]>1){v[pos[l]]=0;break;}
        }
    }
    if(pos[l]!=pos[r]){
        if(!v[pos[r]]){
            for(ll i=(pos[r]-1)*blo+1;i<=r;i++){
                sum[pos[i]]-=a[i];
                a[i]=sqrt(a[i]);
                sum[pos[i]]+=a[i];
            }
            v[pos[r]]=1;
            for(ll i=(pos[r]-1)*blo+1;i<=min(pos[r]*blo,n);i++){
                if(a[i]>1){v[pos[r]]=0;break;}
            }
        }
    }
    for(ll i=pos[l]+1;i<=pos[r]-1;i++) change(i);
}

ll query(ll l,ll r){
    ll ans=0;
    for(ll i=l;i<=min(pos[l]*blo,r);i++) ans+=a[i];
    if(pos[l]!=pos[r])
        for(ll i=(pos[r]-1)*blo+1;i<=r;i++) ans+=a[i];
    for(ll i=pos[l]+1;i<=pos[r]-1;i++) ans+=sum[i];
    return ans;
}

int main()
{
    scanf("%lld",&n);blo=sqrt(n);
    for(ll i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        pos[i]=(i-1)/blo+1;
        sum[pos[i]]+=a[i];
    }
    scanf("%lld",&m);
    ll opt,l,r;
    while(m--){
        scanf("%lld%lld%lld",&opt,&l,&r);
        if(l>r) swap(l,r);
        if(opt==0) update(l,r);
        else printf("%lld
",query(l,r));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/owencodeisking/p/9736440.html