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


    题目描述

    给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,区间求和。

    输入格式

    第一行输入一个数字 n

    第二行输入 n 个数字,第 i 个数字为 ai,以空格隔开。

    接下来输入 n 行询问,每行输入四个数字 opt、l、r、c,以空格隔开。

    若 opt=0,表示将位于 [l,r]的之间的数字都加 c

    若 opt=1,表示询问位于 [l,r]的所有数字的和 mod(c+1)。

    输出格式

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

    样例

    样例输入

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

    样例输出

    1
    4
    思路:分块,不是整块的暴力加,整块的用sum[]数组维护整块的和。
    代码:
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=5e4+10;
    ll a[maxn],b[maxn],sum[maxn],pos[maxn],n,block;
    ll ans;
    void update(int l,int r,int c)//更新 
    {
        for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左边不完整的块暴力加,sum数组维护 
        {
            sum[pos[i]]+=c;
            a[i]+=c;
        }
        if(pos[l]!=pos[r])
        {
            for(int i=(pos[r]-1)*block+1;i<=r;i++)//右边不完整的块暴力加,sum数组维护 
            {
                sum[pos[i]]+=c;
                a[i]+=c;    
            }
        }    
        for(int i=pos[l]+1;i<=pos[r]-1;i++)//整块的b数组保存 
            b[i]+=c;
    } 
    
    ll query(int l,int r,int mod)//查询 
    {
        ans=0;
        for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左边不完整的块,暴力加每个数 
            ans+=a[i]+b[pos[l]];
        if(pos[l]!=pos[r])
        {
            for(int i=(pos[r]-1)*block+1;i<=r;i++)//右边不完整的块,暴力加每个数
                ans+=a[i]+b[pos[r]];
        }
        for(int i=pos[l]+1;i<=pos[r]-1;i++)//完整的块整块加 
        {
            ans+=sum[i]+b[i]*block;
        }
        return ans%mod;
    }
    
    int main()
    {
        scanf("%d",&n);
        memset(sum,0,sizeof(sum));
        block=sqrt(n);//块的大小 
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            pos[i]=(i-1)/block+1;
            sum[pos[i]]+=a[i];//sum数组维护整块的和 
        } 
        int opt,l,r,c;
        for(int i=0;i<n;i++)    
        {
            scanf("%d%d%d%d",&opt,&l,&r,&c);
            if(opt==0)
                update(l,r,c);
            else if(opt==1)
                printf("%lld
    ",query(l,r,c+1));
        }
        return 0;
    }

    第5题:

    思路:这里有个小思维,因为数的大小小于2^31,最多开方5次就为1。所以我们做法和上面一样有sum数组维护整块的值,

    计算整块的时候,用flag记录整组都为1的情况,如果全为1就加block(块的大小),不是就暴力加,不会超时的,因为最多5次开方

    就全为1了,时间复杂度和上面的一样(5倍对时间复杂度没关系)

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #define inf 0x3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=5e4+10;
    ll a[maxn],pos[maxn],sum[maxn],flag[maxn],block,n;
    //a数组存储数字,pos记录每个数在哪个块,sum维护整块的和,flag标记整块是否全为1,blocK块的大小。 
    void update(int l,int r)//更新 
    {
        for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左边不完整块,暴力加。 
        { 
            sum[pos[l]]-=a[i];
            a[i]=sqrt(a[i]);
            sum[pos[l]]+=a[i];//sum数组记得改动 
        }
        if(pos[l]!=pos[r])//右边不完整块,暴力加。
        {
            for(int i=(pos[r]-1)*block+1;i<=r;i++)
            {
                sum[pos[r]]-=a[i];
                a[i]=sqrt(a[i]);
                sum[pos[r]]+=a[i];
            }
        }
        for(int i=pos[l]+1;i<=pos[r]-1;i++)//整块 
        {
            if(flag[i])//全为1,直接跳过 
                continue;
            else//不是全为1,暴力处理 
            {
                flag[i]=1;//先假设全为1,后面如果不是改为0 
                for(int j=(i-1)*block+1;j<=i*block;j++)
                {
                    sum[i]-=a[j];
                    a[j]=sqrt(a[j]);
                    sum[i]+=a[j];
                    if(a[j]!=1)//不是全为1 
                        flag[i]=0;
                }
            }
        }
    }
    
    void solve(int l,int r)
    {
        ll ans=0;
        for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左边不完整块,暴力加 
            ans+=a[i];
        if(pos[l]!=pos[r])
        {
            for(int i=(pos[r]-1)*block+1;i<=r;i++)//右边不完整块,暴力加 
                ans+=a[i];
        }
        for(int i=pos[l]+1;i<=pos[r]-1;i++)//完整块直接加,整块的和即可 
            ans+=sum[i];
        printf("%lld
    ",ans);
    }
    int main()
    {
        scanf("%d",&n);
        block=sqrt(n);//块的大小 
        memset(flag,0,sizeof(flag));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            pos[i]=(i-1)/block+1;
            sum[pos[i]]+=a[i];//每块的和 
        }
        int opt,l,r,c;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d%d",&opt,&l,&r,&c);
            if(opt==0)
                update(l,r);
            else
                solve(l,r);
        }
        return 0;
    }
  • 相关阅读:
    Penetration Test
    Penetration Test
    Penetration Test
    Penetration Test
    Penetration Test
    Penetration Test
    Penetration Test
    Java编程入门(图片)
    第七章:Java基础类库
    第六章:多线程综合案例
  • 原文地址:https://www.cnblogs.com/xiongtao/p/9780262.html
Copyright © 2020-2023  润新知