zoukankan      html  css  js  c++  java
  • #6281. 数列分块入门 5

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

    题目描述

    给出一个长为 nn 的数列 a_1ldots 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(lle ile r),: a_i ← leftlfloor sqrt{a_i} ight floorai(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);
        }
    }
    当初的梦想实现了吗,事到如今只好放弃吗~
  • 相关阅读:
    我的浏览器收藏夹分类
    我的浏览器收藏夹分类
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 315 计算右侧小于当前元素的个数
    Java实现 LeetCode 315 计算右侧小于当前元素的个数
  • 原文地址:https://www.cnblogs.com/caijiaming/p/10327152.html
Copyright © 2011-2022 走看看