#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;
}
原文地址:https://www.cnblogs.com/xiongtao/p/9780262.html