分块基础

分块:把一个长度为n的数列a[]分成sqrt(n)块,每块长度为bl=sqrt(n),用b[i]纪录第i个数a[i]属于第几块,b[i]=(i-1)/bl+1;
操作或查询通常为 4 步:
1.判断要操作或是查询的区间是否在一个块内
2.若在一个块内,暴力操作或查询
3.若不在一个块内,将除了最左边和最右边这两个块外其余的块进行整体的操作,即直接对块打上修改标记之类的
4.单独暴力处理最左边的块和最右边的
复杂度:n*√n,查询o(1);
基础题1:长度为n的数列,区间修改,单点查询

题目传送门

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+50;
int n,m,opt,bl,l,r,k;
int a[N],b[N],fl[N];//fl为块的值,类似懒标记
void add(int l,int r,int k)
{
    for(int i=l;i<=min(r,b[l]*bl);i++)//左边不完整的
        a[i]+=k;
    for(int i=b[l]+1;i<=b[r]-1;i++)//完整块
        fl[i]+=k;
    if(b[l]!=b[r])
        for(int i=(b[r]-1)*bl+1;i<=r;i++)//右边不完整块
            a[i]+=k;
}
int main()
{
     scanf("%d",&n);
         bl=sqrt(n);
         for(int i=1;i<=n;i++){
             scanf("%d",&a[i]);
             b[i]=(i-1)/bl+1;
         }

         for(int i=1;i<=n;i++)
         {
             scanf("%d %d %d %d",&opt,&l,&r,&k);
             if(opt==0)
                 add(l,r,k);
             else
                 printf("%d\n",a[r]+fl[b[r]]);
         }
    return 0;
}

基础题2:给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。
题目传送门

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+50;
int n,m,opt,bl,l,r,k;
int a[N],b[N],fl[N];
vector<int >g[505];
void reset(int s)
{
    int j=b[s];
    g[j].clear();
    for(int i=(j-1)*bl+1;i<=min(n,j*bl);i++)
        g[j].push_back(a[i]);
    sort(g[j].begin(),g[j].end());
}
void add(int l,int r,int k)
{
    for(int i=l;i<=min(r,b[l]*bl);i++)
        a[i]+=k;
    reset(l);
    for(int i=b[l]+1;i<=b[r]-1;i++)
        fl[i]+=k;
    if(b[l]!=b[r])
        for(int i=(b[r]-1)*bl+1;i<=r;i++)
            a[i]+=k;
        reset(r);
}
int query(int l,int r,int c)
{
    int ans=0;
    for(int i=l;i<=min(r,b[l]*bl);i++)
        if(a[i]+fl[b[i]]<c*c)ans++;
    for(int i=b[l]+1;i<=b[r]-1;i++){
        int t=lower_bound(g[i].begin(),g[i].end(),c*c-fl[i])-g[i].begin();
        ans+=t;
    }
    if(b[l]!=b[r])
        for(int i=(b[r]-1)*bl+1;i<=r;i++)
            if(a[i]+fl[b[i]]<c*c)ans++;
            return ans;
}
int main()
{
     scanf("%d",&n);
         bl=sqrt(n);
         for(int i=1;i<=n;i++){
             scanf("%d",&a[i]);
             b[i]=(i-1)/bl+1;
             g[b[i]].push_back(a[i]);
         }
         int num=(n-1)/bl+1;
         for(int i=1;i<=num;i++)
         {
             sort(g[i].begin(),g[i].end());
         }
         for(int i=1;i<=n;i++)
         {
             scanf("%d %d %d %d",&opt,&l,&r,&k);
             if(opt==0)
                 add(l,r,k);
             else
                 printf("%d\n",query(l,r,k));
         }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值