#6281. 数列分块入门 5

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

题目描述

给出一个长为 nn 的数列 a_1\ldots a_na1an,以及 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(l\le i\le r),\: a_i ← \left\lfloor \sqrt{a_i}\right\rfloorai(lir),aiai

若 \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}-1ans2311。

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

假设最大 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);
    }
}

 

转载于:https://www.cnblogs.com/caijiaming/p/10327152.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值