数列分块入门九题(一):LOJ6277~6279

Preface

分块,一个神奇的暴力算法。能够把不少\(O(n^2)\)数据结构题的暴力优化到常数极小\(O(n\sqrt n)\)。当一些毒瘤题没法用线段树,主席树,平衡树,树状数组......\(O(n\ logn)\)方法写出时固然在你不会写这些算法的时候用大力分块骗分是再好不过的方法了!git

固然分块的大体思想比较简单,咱们看一下:算法

/Here is Pic 1数组

可是因为分块在实际应用中有不一样的方法,因此让咱们来找各类各样的板子题来练练手吧。数据结构


数列分块入门 1——区间加法,单点查值

这道题对于几乎全部的线性数据结构来讲,这应该算是一个必修的操做了吧。优化

对于分块,咱们通常就考虑两个问题:spa

  1. 对于块内的元素如何高效地操做(\(O(1)\)\(O(log\ n)\)
  2. 对于快外的元素怎么暴力处理(\(O(\sqrt n)\)\(O(log\ \sqrt n)\)

对于第一个问题,咱们对每一个完整的块打一个标记,而后两端不完整的部分咱们直接遍历更新便可。code

查询的时候讲它自己的值和它所在块的标记相加便可。排序

CODEit

#include<cstdio>
#include<cctype>
#include<cmath>
using namespace std;
const int N=50005,BLO=250;
int n,a[N],size,blk[N],mark[BLO],opt,x,y,z;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag;
}
inline void write(int x)
{
    if (x>9) write(x/10);
    putchar(x%10+'0');
}
inline int min(int a,int b)
{
    return a<b?a:b;
}
inline int query(int x)
{
    return mark[blk[x]]+a[x];
}
inline void modify(int l,int r,int k)
{
    register int i;
    for (i=l;i<=min(blk[l]*size,r);++i) a[i]+=k;
    if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) a[i]+=k;
    for (i=blk[l]+1;i<=blk[r]-1;++i) mark[i]+=k;
}
int main()
{
    //freopen("1.in","r",stdin); freopen("1.out","w",stdout);
    register int i; read(n); size=sqrt(n);
    for (i=1;i<=n;++i)
    read(a[i]),blk[i]=(i-1)/size+1;
    for (i=1;i<=n;++i)
    {
        read(opt); read(x); read(y); read(z);
        if (opt) write(query(y)),putchar('\n'); else modify(x,y,z);
    }
    return 0;
}

数列分块入门 2——区间加法,询问区间内小于某个值的元素个数

这道题其实在没有加法而且离线的状况下能够用主席树解决,但在这里彷佛没有什么好的\(O(n\ logn)\)方法。io

对于分块,区间加法的时候仍是常规的块内打标记+块外暴力更新

可是询问小于某个值的元素个数又是什么鬼?

而后咱们就发现,分块对于通常数据结构的优点就来了,咱们能够对每一块进行排序

而后查询,二分了解一下。不过虽然修改的时候对于整块的顺序没有影响,可是咱们对于两端的暴力就会影响相对顺序。

而后处理方法也很暴力,直接从新排序便可,复杂度约为\(O(n\sqrt n\cdot log\sqrt n)\)

CODE

#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int N=50005,BLO=250;
int n,a[N],size,blk[N],mark[BLO],opt,x,y,z,tot;
vector <int> r[BLO];
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag;
}
inline void write(int x)
{
    if (x>9) write(x/10);
    putchar(x%10+'0');
}
inline int min(int a,int b)
{
    return a<b?a:b;
}
inline int find(int id,int x)
{
    int L=0,R=size-1,res=-1;
    while (L<=R)
    {
        int mid=L+R>>1;
        if (r[id][mid]<x) res=mid,L=mid+1; else R=mid-1;
    }
    return res+1;
}
inline void reset(int id)
{
    register int i; r[id].clear();
    for (i=(id-1)*size+1;i<=id*size;++i)
    r[id].push_back(a[i]);
    sort(r[id].begin(),r[id].end());
}
inline void modify(int l,int r,int x)
{
    register int i;
    for (i=l;i<=min(blk[l]*size,r);++i)
    a[i]+=x; reset(blk[l]);
    if (blk[l]!=blk[r])
    {
        for (i=(blk[r]-1)*size+1;i<=r;++i)
        a[i]+=x; reset(blk[r]);
    }
    for (i=blk[l]+1;i<=blk[r]-1;++i) mark[i]+=x;
}
inline int query(int l,int r,int x)
{
    register int i; int ans=0;
    for (i=l;i<=min(blk[l]*size,r);++i) (a[i]+mark[blk[l]]<x)&&++ans;
    if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) (a[i]+mark[blk[r]]<x)&&++ans;
    for (i=blk[l]+1;i<=blk[r]-1;++i) ans+=find(i,x-mark[i]); 
    return ans;
}
int main()
{
    //freopen("2.in","r",stdin); freopen("2.out","w",stdout);
    register int i; read(n); size=sqrt(n); tot=(n-1)/size+1;
    for (i=1;i<=n;++i)
    read(a[i]),r[blk[i]=(i-1)/size+1].push_back(a[i]);
    for (i=1;i<=tot;++i)
    sort(r[i].begin(),r[i].end());
    for (i=1;i<=n;++i)
    {
        read(opt); read(x); read(y); read(z);
        if (opt) write(query(x,y,z*z)),putchar('\n'); else modify(x,y,z);
    }
    return 0;
}

数列分块入门 3——区间加法,询问区间内小于某个值的前驱

这道题有了上一道题的思路就很简单了。

几乎是同样的方法我是直接改了下上面的代码的,只不过是二分的几个细节改了下而已

CODE

#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int N=100005,BLO=320;
int n,a[N],size,blk[N],mark[BLO],opt,x,y,z,tot;
vector <int> r[BLO];
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag;
}
inline void write(int x)
{
    if (x<0) putchar('-'),x=-x;
    if (x>9) write(x/10); putchar(x%10+'0');
}
inline int min(int a,int b)
{
    return a<b?a:b;
}
inline int max(int a,int b)
{
    return a>b?a:b;
}
inline int find(int id,int x)
{
    int L=0,R=size-1,res=-1;
    while (L<=R)
    {
        int mid=L+R>>1;
        if (r[id][mid]<x) res=r[id][mid],L=mid+1; else R=mid-1;
    }
    return ~res?res+mark[id]:-1;
}
inline void reset(int id)
{
    register int i; r[id].clear();
    for (i=(id-1)*size+1;i<=id*size;++i)
    r[id].push_back(a[i]);
    sort(r[id].begin(),r[id].end());
}
inline void modify(int l,int r,int x)
{
    register int i;
    for (i=l;i<=min(blk[l]*size,r);++i)
    a[i]+=x; reset(blk[l]);
    if (blk[l]!=blk[r])
    {
        for (i=(blk[r]-1)*size+1;i<=r;++i)
        a[i]+=x; reset(blk[r]);
    }
    for (i=blk[l]+1;i<=blk[r]-1;++i) mark[i]+=x;
}
inline int query(int l,int r,int x)
{
    register int i; int ans=-1;
    for (i=l;i<=min(blk[l]*size,r);++i) (a[i]+mark[blk[l]]<x)&&(ans=max(ans,a[i]+mark[blk[l]]));
    if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) (a[i]+mark[blk[r]]<x)&&(ans=max(ans,a[i]+mark[blk[r]]));
    for (i=blk[l]+1;i<=blk[r]-1;++i) ans=max(ans,find(i,x-mark[i])); 
    return ans;
}
int main()
{
    //freopen("3.in","r",stdin); freopen("3.out","w",stdout);
    register int i; read(n); size=sqrt(n); tot=(n-1)/size+1;
    for (i=1;i<=n;++i)
    read(a[i]),r[blk[i]=(i-1)/size+1].push_back(a[i]);
    for (i=1;i<=tot;++i)
    sort(r[i].begin(),r[i].end());
    for (i=1;i<=n;++i)
    {
        read(opt); read(x); read(y); read(z);
        if (opt) write(query(x,y,z)),putchar('\n'); else modify(x,y,z);
    }
    return 0;
}
相关文章
相关标签/搜索