分塊

分塊

  • 時間複雜度:n * (根號n)
  • 原理:把一個序列分成一塊塊的,每一塊有三個元素。一個是這個塊表示的序列左邊界left,右邊界right,攜帶的信息。(攜帶的信息可以爲區間最大值、區間和等等)
  • 分塊三部曲
    • 建塊(build)
    • 更新(update)
    • 查詢 (ask)
  • 分塊與其它算法的比較
    • 速度上:樹狀數組 < 線段樹 < 分塊
    • 使用上:分塊通用性很強,樹狀數組還行,線段樹較單一
    • 承受數據上:分塊極限20w,線段樹基本可以過50w,樹狀數組還可以比50w再大一些

建塊

  • sizeBlock : 每一塊的大小
  • numBlock : 塊的數量
  • belong[i] : 表示i屬於哪個塊
  • block結構體 : 即表示“塊”
    • .l : 此塊表示的左邊界
    • .r : 此塊表示的右邊界
    • .val : 此塊表示的信息(下面案例以“區間和”爲例)
void build()
{
    sizeBlock=(int)sqrt(n);
    numBlock=n/sizeBlock;
    if(n%sizeBlock) numBlock++;
    for(int i=1;i<=numBlock;i++)
        block[i].l=(i-1)*sizeBlock+1,block[i].r=i*sizeBlock;
    block[numBlock].r=n;
    for(int i=1;i<=n;i++) belong[i]=(i-1)/sizeBlock+1;

    for(int i=1;i<=numBlock;i++)
        for(int j=block[i].l;j<=block[i].r;j++)
            block[i].val+=a[j];
}

更新

  • 單點更新
  • 區間更新

單點更新(以“區間和”爲例)

void update(int pos,int add) //函數功能:pos位置增加add
{
    a[pos]+=add; //直接加
    block[belong[pos]].val+=add; //pos這個元素所在塊的區間和更新
}

區間更新(以“區間和爲例”)

void update(int ll,int rr,int add) //函數功能:ll-rr位置每個元素加add
{
    if(belong[ll]==belong[rr]) //特判:如果ll、rr屬於同一塊,直接暴力更新
    {
        for(int i=ll;i<=rr;i++)
            a[i]+=add;
        return;
    }
    for(int i=ll;i<=block[belong[ll]].r;i++) a[i]+=add; //左邊不成一個塊的暴力更新
    for(int i=block[belong[rr]].l;i<=rr;i++) a[i]+=add; //右邊不成一個塊的暴力更新
    for(int i=belong[ll]+1;i<belong[rr];i++) block[i].tag+=add;
    //成塊的情況,每塊的標記域更新
    //最後塊中的元素即爲 : 現元素 + 標記域
}

查詢

  • 單點查詢
  • 區間查詢

單點查詢

int ask(int pos) //函數功能:返回pos位置的元素
{
    return a[pos]
}

區間查詢(以“區間和”爲例)

int ask(int ll,int rr) //函數功能:返回ll-rr區間的區間和
{
    int ans=0;
    if(belong[ll]==belong[rr])
    {
        for(int i=ll;i<=rr;i++)
            ans+=a[i];
        return ans;
    }
    for(int i=ll;i<=block[belong[ll]].r;i++) ans+=a[i];
    for(int i=block[belong[rr]].l;i<=rr;i++) ans+=a[i];
    for(int i=belong[ll]+1;i<belong[rr];i++) ans+=block[i].val;
    return ans;
}

總結

  • 注意區間更新與區間查詢查詢都是一個套路。先特判在同一塊的情況,然後去處理左邊多的和右邊多的,最後處理中間的塊
  • 有些分塊題不一定按照模版寫。但是分塊的思想是亙古不變的

補充

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

struct Block{long long l,r,tag,val,len;}block[200005];
long long n,q,sizeBlock,numBlock;
long long a[200005],belong[200005];

void build()
{
    sizeBlock=(long long)sqrt(n);
    numBlock=n/sizeBlock;
    if(n%sizeBlock) numBlock++;
    for(long long i=1;i<=numBlock;i++)
        block[i].l=(i-1)*sizeBlock+1,block[i].r=i*sizeBlock;
    block[numBlock].r=n;
    for(long long i=1;i<=n;i++) belong[i]=(i-1)/sizeBlock+1;

    for(long long i=1;i<=numBlock;i++)
        for(long long j=block[i].l;j<=block[i].r;j++)
            block[i].val+=a[j];
    for(long long i=1;i<=numBlock;i++)
        block[i].len=block[i].r-block[i].l+1;
}

void update(long long ll,long long rr,long long add)
{
    if(belong[ll]==belong[rr])
    {
        for(long long i=ll;i<=rr;i++) a[i]+=add;
        block[belong[ll]].val+=(add*(rr-ll+1));
        return;
    }
    for(long long i=ll;i<=block[belong[ll]].r;i++) a[i]+=add,block[belong[i]].val+=add;
    for(long long i=block[belong[rr]].l;i<=rr;i++) a[i]+=add,block[belong[i]].val+=add;
    for(long long i=belong[ll]+1;i<belong[rr];i++) block[i].tag+=add;
    for(long long i=belong[ll]+1;i<belong[rr];i++) block[i].val+=(block[i].len*add);
}

long long ask(long long ll,long long rr)
{
    long long ans=0;
    if(belong[ll]==belong[rr])
    {
        for(long long i=ll;i<=rr;i++) ans+=(a[i]+block[belong[ll]].tag);
        return ans;
    }
    for(long long i=ll;i<=block[belong[ll]].r;i++) ans+=(a[i]+block[belong[i]].tag);
    for(long long i=block[belong[rr]].l;i<=rr;i++) ans+=(a[i]+block[belong[i]].tag);
    for(long long i=belong[ll]+1;i<belong[rr];i++) ans+=block[i].val;
    return ans;
}

int main()
{
    cin>>n;
    for(long long i=1;i<=n;i++) scanf("%lld",&a[i]);
    build();
    cin>>q;
    for(long long i=1;i<=q;i++)
    {
        long long op,x,y,z;  scanf("%lld",&op);
        if(op==1) scanf("%lld%lld%lld",&x,&y,&z),update(x,y,z);
        else if(op==2) scanf("%lld%lld",&x,&y),printf("%lld\n",ask(x,y));
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章