分块大法好:数列分块入门1~9

本文详细介绍了数列分块的概念和应用,通过9个实例逐步讲解,包括区间加法、查询、乘法等操作。文章推荐了机房大佬的通俗易懂代码,并强调分块算法在处理大规模数据时的高效性,适合初学者进阶学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

艰苦地刷了4天半的分块
深感分块是一个非常巧(暴)妙(力) 的算法
如果有觉得hzwer的代码太奇妙(看不懂)的推荐一下机房大佬的通俗易懂的代码:
http://www.cnblogs.com/CHerish_OI/category/1176577.html(此处手动艾特cherish_oi同学)
http://hzwer.com/8053.html

loj#6277. 数列分块入门 1

https://loj.ac/problem/6277

区间加法,单点查询

基础的东西
数学老师曰:基本知识不熟悉,难题不会做!
分块 每个块的大小都是sqrt(n)
n=12 N=sqrt(12) 向下取整 3
第一块:1~3 第二块:4~6 第三块:7~9 第四块: 10~12
属于第几块pos[i]=(i-1)/N+1;
如果是完整的块就直接更新到标记里
不完整的块就直接暴力更新就ok

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int m,pos[51000];
int s[51000],tag[51000];
void change(int a,int b,int c)
{
    for (int i=a;i<=min(b,pos[a]*m);i++) s[i]+=c;//如果b在a的块内就更新到b 否则更新到a的块的结尾
    if (pos[a]!=pos[b])
    {
        for (int i=(pos[b]-1)*m+1;i<=b;i++) s[i]+=c; //如果ab不同块 更新b的块内的开头到b 
    } 
    for (int i=pos[a]+1;i<=pos[b]-1;i++) tag[i]+=c;
}
int main()
{
    int n;
    scanf("%d",&n);
    m=sqrt(n);
    for (int i=1;i<=n;i++) pos[i]=(i-1)/m+1;
    for (int i=1;i<=n;i++) scanf("%d",&s[i]);
    for (int i=1;i<=n;i++)
    {
        int x,a,b,c;
        scanf("%d%d%d%d",&x,&a,&b,&c);
        if (x==0)
        {
            change(a,b,c);
        }
        else if (x==1) 
        {
            printf("%d\n",tag[pos[b]]+s[b]);
        }
    }
    return 0;
}

loj#6278. 数列分块入门 2

https://loj.ac/problem/6278

区间加法,查询比x小的个数

排序维护一下块内的递增性
完整的块可以直接用lower_bound返回一下第一个大于等于x的位置
不完整的块还是直接暴力
非常不争气的写了STL

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
int n,N;
int pos[51000],s[51000],tag[51000];
vector<int> v[510];
void update(int x)
{
    v[x].clear();
    for (int i=(x-1)*N+1;i<=min(x*N,n);i++)//有可能这是最后一个块 min一下
        v[x].push_back(s[i]);//push_back:压进容器尾部 
    sort(v[x].begin(),v[x].end());//从容器头到容器尾排序 
}
void add(int x,int y,int c)
{
    for (int i=x;i<=min(pos[x]*N,y);i++)//x,y有可能同块 否则更新完x块剩下整个快 
        s[i]+=c;
    update(pos[x]);
    if (pos[x]!=pos[y]) 
    {
        for (int i=(pos[y]-1)*N+1;i<=y;i++) //更新y所在的不完整块 
            s[i]+=c;
        update(pos[y]);
    }
    for (int i=pos[x]+1;i<pos[y];i++) tag[i]+=c; 
}
int getsum(int x,int y,int c)//x~y中统计小于c个数 
{
    int ans=0;
    for (int i=x;i<=min(pos[x]*N,y);i++) 
        if (s[i]+tag[pos[x]]<c) ans++; //xif (pos[x]!=pos[y])//y
    {
        for (int i=((pos[y]-1)*N+1);i<=y;i++) if (s[i]+tag[pos[y]]<c) ans++; 
    }
    for (int i=pos[x]+1;i<pos[y];i++)
    {
        int t=c-tag[i];
        ans+=lower_bound(v[i].begin(),v[i].end(),t)-v[i].begin();//lower_bound:返回x的后继(第一个比x大于等于的位置)(v内有序) 
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    N=sqrt(n);
    for (int i=1;i<=n;i++) scanf("%d",&s[i]);
    for (int i=1;i<=n;i++)
    {
        pos[i]=(i-1)/N+1;
        v[pos[i]].push_back(s[i]);
    }
    for (int i=1;i<=pos[n];i++)
        sort(v[i].begin(),v[i].end());
    for (int i=1;i<=n;i++)
    {
        int u,x,y,c;
        scanf("%d%d%d%d",&u,&x,&y,&c);
        if (u==0) add(x,y,c);
        else if (u==1) printf("%d\n",getsum(x,y,c*c));
    }
    return 0;
}

loj#6279. 数列分块入门 3

https://loj.ac/problem/6279

区间加法,求某个数的前驱
用set维护方便很多(其实相当于一颗splay把我觉得,手动艾特lynkin同学用的二分查找前驱过了)
依旧是完整的块直接lower_bound
不完整的块还是继续暴力
不过要注意set的使用方法 删掉再进行暴力修改 再加回去
依旧不争气地继续用了STL

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
set<int> tr[1100];//set相当于一颗树
int s[110000],tag[110000],pos[110000];
int n,N;
void add(int x,int y,int c)
{
    for (int i
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值