#6281. 数列分块入门 5

Posted caijiaming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#6281. 数列分块入门 5相关的知识,希望对你有一定的参考价值。

题目链接:https://loj.ac/problem/6281

题目描述

给出一个长为 nn 的数列 a_1ldots a_na1?an?,以及 nn 个操作,操作涉及区间开方,区间求和。

输入格式

第一行输入一个数字 nn。

第二行输入 nn 个数字,第 ii 个数字为 a_iai?,以空格隔开。

接下来输入 nn 行询问,每行输入四个数字 mathrm{opt}, l, r, copt,l,r,c,以空格隔开。

若 mathrm{opt} = 0opt=0,表示将位于 [l, r][l,r] 的之间的数字都开方。对于区间中每个 a_i(lle ile r),: a_i ← leftlfloor sqrt{a_i} ight floorai?(lir),ai??ai???

若 mathrm{opt} = 1opt=1,表示询问位于 [l, r][l,r] 的所有数字的和。

输出格式

对于每次询问,输出一行一个数字表示答案。

样例

样例输入

4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4

样例输出

6
2

数据范围与提示

对于 100\%100% 的数据,1 leq n leq 50000, -2^{31} leq mathrm{others}1n50000,?231others、mathrm{ans} leq 2^{31}-1ans231?1。

思路:这题棘手的地方在与块中开方的处理,怎么处理呢?   一个区间里每一个数的开方,这个要想不遍历一遍很难,但是遍历的话还要分块干嘛呢?  问题就在开方这个字眼,题目中给的范围里的数,

假设最大 2^32  最多开方6次就变为0或者1了  一个数变为0或者1 他再开方就不会再变化了,试想一下,假如一个区间里所有的数都变为了0或者1  那么还要处理吗  显然是不用的,所以我们就记录哪些区间

里的所有的数都变为了0或者1  是的话就不用处理这个块了,这就是分块在这里的巧妙之处了!!! 下面看代码:

#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
const int maxn=50000+5;
int a[maxn];
int block;
int bl[maxn];
int sum[maxn];
int flag[maxn];
void Updata_bl(int x)
{
    if(flag[x]) return ;
    flag[x]=1;
    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]=0;
    }
}
void Updata(int l,int r)
{
    for(int i=l;i<=min(bl[l]*block,r);i++)
    {
        sum[bl[i]]-=a[i];
        a[i]=sqrt(a[i]);
        sum[bl[i]]+=a[i];
    }
    if(bl[l]!=bl[r])
    {
        for(int i=(bl[r]-1)*block+1;i<=r;i++)
        {
            sum[bl[r]]-=a[i];
            a[i]=sqrt(a[i]);
            sum[bl[r]]+=a[i];
        }
    }
    for(int i=bl[l]+1;i<=bl[r]-1;i++)
    {
        Updata_bl(i);
    }
}
void Query(int l,int r)
{
    int ans=0;
    for(int i=l;i<=min(bl[l]*block,r);i++) ans+=a[i];
    if(bl[l]!=bl[r])
    {
        for(int i=(bl[r]-1)*block+1;i<=r;i++) ans+=a[i];
    }
    for(int i=bl[l]+1;i<=bl[r]-1;i++) ans+=sum[i];
    cout<<ans<<endl;
}
int main()
{
    int n;
    int opt,l,r,c;
    cin>>n ;
    memset(sum,0,sizeof(sum));
    memset(flag,0,sizeof(flag));
    block=sqrt(n);
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++)
    {
        bl[i]=(i-1)/block+1;
        sum[bl[i]]+=a[i];
    }
    for(int i=1;i<=n;i++)
    {
        cin>>opt>>l>>r>>c;
        if(opt==0) Updata(l,r);
        else Query(l,r);
    }
}

 

以上是关于#6281. 数列分块入门 5的主要内容,如果未能解决你的问题,请参考以下文章

#6280. 数列分块入门 4 #6281. 数列分块入门 5

#6281. 数列分块入门 5

LOJ.6281.数列分块入门5(分块 区间开方)

LOJ#6281. 数列分块入门 5

数列分块入门5 解题报告

数列分块入门7 解题报告