日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

线性结构 —— 分块算法

發布時間:2025/3/17 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线性结构 —— 分块算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【概述】

所謂分塊,即對于一個長度為 n 的序列,設塊的大小為 S,從序列的第一個元素起,每 S 個元素被分成一塊,若剩余的元素不足 S 個,令他們組成一塊。經過分塊后的數組,稱為塊狀數組,在塊狀數組的基礎上加以擴展,即可得到塊狀鏈表

在一個區間操作時,完整包含于區間的塊稱為整塊,只有部分包含于區間的塊,稱為不完整的塊,不完整的塊實質上行即為區間左右端點所在的兩個塊。

在許多區間問題中,使用樹型結構,如:樹狀數組、線段樹等會更加優秀,但當所需的某些信息無法快速合并時,就只能使用分塊,例如:詢問一個區間的眾數

需要注意的是,利用來解決的問題,大部分都是要求強制在線的,此外,在時間復雜度上:樹狀數組?> 線段樹 > 塊狀數組(分塊)

但三者各有優劣:

  • 樹狀數組:代碼簡單、常數小
  • 線段樹:代碼復雜、功能強大、常數大
  • 塊狀數組:代碼復雜、常數大、能維護一些難以快速合并的數據

【基本原理】

從時間復雜度來考慮,一般來說,分塊長度為 sqrt(n),那么,初始化時有:

int block,sum;//block為塊的長度,sum為塊的個數 int a[N];//存放數列元素 int pos[N],tag[N];//pos記錄第i個元素在第幾個塊中,tag為操作標記 int ans[N];//維護整塊和 void init(){//初始化block=(int)sqrt(n) //塊的長度sum=n/block;//塊數if(n%block)sum++;for(int i=1;i<=n;i++)//第i個元素在第幾塊中pos[i]=(i-1)/block+1; }

對于區間查詢,有三種情況:

  • 詢問區間 [x,y] 中,x 不是塊的左邊界,y 也不是塊的右邊界
  • 詢問區間 [x,y] 中,x 不是塊的左邊界,y 是塊的右邊界
  • 詢問區間 [x,y] 中,x 是塊的左邊界,y 不是塊的右邊界
  • 由于可以預處理每一塊的值,因此對于一塊來說,查詢的時間復雜度是 O(1) 的,對于多出來的邊角料的部分總和不會超過 2*sqrt(n),基于這個時間復雜度,進行暴力求解,可以發現對于第一種情況,q?次查詢的復雜度為 O(q*sqrt(n)),對于第二、三中情況,完全可以當成情況 1 來處理

    對于一個塊的左邊界,可以求出上一個塊的右邊界 (pos[x]-1)*block,那么 +1 后就是這個塊的左邊界,即:(pos[x]-1)*block+1

    因此,在每次詢問時,第 i 個元素 a[i] 處于第 (i-1)/sqrt(n)+1 塊,其左端點為 (i-1)*sqrt(n)+1,右端點為 min(i*sqrt(n),n),返回元素的值再加上其所在塊的標記即可。

    int block,sum;//block為塊的長度,sum為塊的個數 int a[N];//存放數列元素 int pos[N],tag[N];//pos記錄第i個元素在第幾個塊中,tag為操作標記 int ans[N];//維護整塊和 int query(int L,int R){for(int i=L;i<=min(R,pos[L]*block);i++)//處理左邊角料//do somethingfor(int i=(pos[R]-1)*block+1;i<=R;i++)//處理右邊角料//do somethingfor(int i=pos[L]+1;i<=pos[R]-1;i++)//處理中間k塊//do something }

    對于修改操作,與查詢操作一樣,同樣分成三部分,對于邊角料暴力修改 a[i],對于每一塊,修改每一塊的標記值 tag[pos[i]],那么,對于 m 次修改,總時間復雜度為 O(m*sqrt(n))?

    int block,sum;//block為塊的長度,sum為塊的個數 int a[N];//存放數列元素 int pos[N],tag[N];//pos記錄第i個元素在第幾個塊中,tag為操作標記 void change(int x){//do something } void update(int L,int R,int x){for(int i=L;i<=min(R,pos[L]*block);i++)//處理左邊角料,對a[i]進行操作change(x);if(pos[L]!=pos[R])//存在右區間才遍歷,防止重復計算for(int i=(pos[R]-1)*block+1;i<=R;i++)//處理右邊角料,對a[i]進行操作change(x);for(int i=pos[L]+1;i<=pos[R]-1;i++)//處理中間k塊,對tag[i]進行操作change(x); }

    ?而對于整塊的預處理,即使每個塊暴力進行對 ans[i] 的預處理,時間復雜度也只有 O(n)

    int ans[N];//維護整塊 void work(int L,int R,int x){int start=0;for(int i=L;i<=R;i++)//do something with startans[x]=start;//整塊的標記 }int mian(){...for(int i=1;i<=sum;i++)//傳入塊的左右邊界與編號work((i-1)*block+1,i*block,i);...}

    【模版&例題】

    模版原理:分塊九講

    • 數列分塊入門 1(LibreOj-6277)(區間加法,單點詢問):點擊這里
    • 數列分塊入門 2(LibreOj-6278)(區間加法,詢問區間內小于某個值 x 的元素個數):點擊這里
    • 數列分塊入門 3(LibreOj-6279)(區間加法,詢問區間內小于某個值 x 的前驅):點擊這里
    • 數列分塊入門 4(LibreOj-6280)(區間加法,詢問區間和):點擊這里
    • 數列分塊入門 5(LibreOj-6281)(區間開方,詢問區間和):點擊這里
    • 數列分塊入門 6(LibreOj-6282)(單點插入,單點詢問):點擊這里
    • 數列分塊入門 7(LibreOj-6283)(區間乘法與加法,單點詢問):點擊這里
    • 數列分塊入門 8(LibreOj-6284)(詢問區間某值個數,區間賦值):點擊這里
    • 數列分塊入門 9(LibreOj-6285)(詢問區間眾數):點擊這里

    總結

    以上是生活随笔為你收集整理的线性结构 —— 分块算法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。