分块学习小结2

咳咳咳。因为发现最近许多区间题目都可以用莫队来水分,然而我太弱,学习莫队时总是一脸蒙圈,发现莫队里有分块的东东,就又来复习分块了。


切入正题

上一次学习了如何区间修改与单点查询,这次是跨越好大。。。


题目记录

给一个长度为n的序列,操作涉及到区间加法,和求某区间内数字大小不超过x的数的个数。
数据范围:
n<=10000;操作完后所有a[i]<=2^31-1;

粗略一看线段树之类的应该解决不了吧?(。・・)ノ(大犇别打我)
然而分块却可以(≧▽≦)/!


分块解法

还是老样子,按 n 分一个块。
首先想想,当没有区间加时,该怎么做?
搜索每一个算(滑稽)
很明显是可以通过二分来找,所以我们需要对每一个块排序,然后对于完整的块直接二分查找,不完整的块就暴力。
时间 O(nlogn) (排序)+ O(nlogn) (查找)。
这里可以让分的块大小不同而提高效率,就不说了。
至于区间加法,对于完整的块直接打下标记;不完整的块就直接暴力,但是这样会影响到块内的单调性,所以需要再排序一次。时间复杂度不计
那么这时查询时,不再是对小于x查询,而是对小于x-lab[i]进行查询,lab[i]表示第i块的标记。


代码

打的好丑啊(。・・)ノ

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)

using namespace std;

const int maxn=1e4+5;
int bl[maxn],n,block,lab[103],m;
struct cy{
    int val,id; //id表示当前数的位置。
}a[maxn];

bool cmp(cy a,cy b)
{
    return a.val<b.val;
}
void add(int l,int r,int x)//区间加。
{
    bool bz1,bz2;
    if (bl[l]==bl[l-1]) bz1=false; else bz1=true;
    if (bl[r]==bl[r+1]) bz2=false; else bz2=true;
    if (bl[l]==bl[r]){
        if (bz1&&bz2) lab[bl[l]]+=x;
        else {
            fo(i,(bl[l]-1*block+1),bl[r]*block)//范围不是l~r,而是这个块!下同。
            if (a[i].id>=l&&a[i].id<=r) a[i].val+=x;//判断位置在不在区间内,因为排序改变了每块内的每个数的位置。
            sort(a+block*(bl[l]-1)+1,a+block*(bl[r]),cmp);
        }
        return;
    }
    if (bz1&&bz2) 
        fo(i,bl[l],bl[r]) 
            lab[i]+=x;
    else if (!bz1&&!bz2){
        fo(i,bl[l]+1,bl[r]-1) 
            lab[i]+=x;
        fo(i,(bl[l]-1)*block+1,bl[l]*block) 
        if (a[i].id>=l&&a[i].id<=r) a[i].val+=x;
        sort(a+(bl[l]-1)*block+1,a+bl[l]*block+1,cmp);
        fo(i,(bl[r]-1)*block+1,bl[r]*block) 
        if (a[i].id>=l&&a[i].id<=r) a[i].val+=x;
        sort(a+(bl[r]-1)*block+1,a+bl[r]*block+1,cmp);
    }
    else if (bz1&&!bz2){
        fo(i,bl[l],bl[r]-1) 
            lab[i]+=x;
        fo(i,(bl[r]-1)*block,bl[r]*block)
        if (a[i].id>=l&&a[i].id<=r) a[i].val+=x;
        sort(a+(bl[r]-1)*block+1,a+bl[r]*block+1,cmp);
    }
    else {
        fo(i,bl[l]+1,bl[r]) 
            lab[i]+=x;
        fo(i,(bl[l]-1)*block+1,bl[l]*block) 
        if (a[i].id>=l&&a[i].id<=r) a[i].val+=x;
        sort(a+(bl[l]-1)*block+1,a+bl[l]*block+1,cmp);
    }
}
int mefind(int l,int r,int x) //块内二分查找。
{
    int now=l;
    while (l<=r){
        int mid=(l+r)/2;
        if (a[mid].val<x-lab[bl[mid]]) l=mid+1;
        else r=mid-1;
    }
    return l-now;
}
int find(int l,int r,int x)//查询。
{
    int sum=0;
    bool bz1,bz2;
    if (bl[l]==bl[l-1]) bz1=false; else bz1=true;
    if (bl[r]==bl[r+1]) bz2=false; else bz2=true;
    if (bl[l]==bl[r]) {
        if (bz1&&bz2) sum=mefind(l,r,x);
        else {
            fo(i,(bl[l]-1)*block+1,bl[r]*block) 
            if (a[i].val+lab[bl[i]]<x&&a[i].id>=l&&a[i].id<=r) ++sum;
        }
        return sum;
    }
    if (bz1&&bz2)
        fo(i,bl[l],bl[r]) 
            sum+=mefind((i-1)*block+1,i*block,x);
    else if (!bz1&&!bz2) {
        fo(i,bl[l]+1,bl[r]-1) sum+=mefind((i-1)*block+1,i*block,x);
        fo(i,(bl[l]-1)*block+1,bl[l]*block) 
        if (a[i].val+lab[bl[i]]<x&&a[i].id>=l&&a[i].id<=r) ++sum;
        fo(i,(bl[r]-1)*block+1,bl[r]*block) 
        if (a[i].val+lab[bl[i]]<x&&a[i].id>=l&&a[i].id<=r) ++sum;
    }
    else if (bz1&&!bz2){
        fo(i,bl[l],bl[r]-1) sum+=mefind((i-1)*block+1,i*block,x);
        fo(i,(bl[r]-1)*block+1,bl[r]*block) 
        if (a[i].val+lab[bl[i]]<x&&a[i].id>=l&&a[i].id<=r) ++sum;
    }
    else {
        fo(i,bl[l]+1,bl[r]) sum+=mefind((i-1)*block+1,i*block,x);
        fo(i,(bl[l]-1)*block+1,bl[l]*block) 
        if (a[i].val+lab[bl[i]]<x&&a[i].id>=l&&a[i].id<=r) ++sum;
    }
    return sum;
}
int main()
{
    freopen("T.in","r",stdin);
    scanf("%d%d",&n,&m);
    block=sqrt(n);
    fo(i,1,n){
        scanf("%d",&a[i].val);
        a[i].id=i;
        bl[i]=(i-1)/block+1;
    }
    bl[0]=1; bl[n+1]=bl[n];
    fo(i,1,block) sort(a+block*(i-1)+1,a+block*i+1,cmp);
    fo(i,1,m){
        int t,l,r,x;
        scanf("%d%d%d%d",&t,&l,&r,&x);
        if (!t) add(l,r,x);
        else printf("%d\n",find(l,r,x));
    }
}

打了好久,虽然拍对了,但没交过题,好虚。。。。
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值