數列分塊入門九題(一):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;
}
相關文章
相關標籤/搜索