歸納(三):分塊

來源:互聯網
上載者:User

標籤:amp   getch   維護   read   arc   char   include   algo   als   

何為分塊

優雅的暴力

思維難度低下,代碼難度低下,非常優秀的一種演算法(?)。
實現方法主要是塊與塊之間 \(O(1)*\sqrt{n}\) 查詢,邊角 \(O(\sqrt{n})\) 暴力查詢。
總複雜度 \(O(\sqrt{n})\)。

代碼實現

首先需要塊的大小 \(block\) ,和每個下標歸屬於哪個塊 \(belong[i]\) 。
如果需要塊內有序,可以使用 \(std::vector\) 。

區間修改對於整塊使用lazy標記的思想,邊邊角角還是\(O(\sqrt{n})\)暴力。

查詢同理。

例題(一):教主的魔法

可以說,這是分塊最最經典的一道題了。
因為似乎沒有其他做法?

連結:教主的魔法

分好塊,用 \(std::vector<int> vc[maxn]\) 維護區間的高矮關係。
用 \(std::lower_bound\) 進行查詢。

修改就打標記。

#include<cstdio>#include<vector>#include<cmath>#include<algorithm>const int maxn=1e6+5;class Divid_Block {    private:        int n,q,delta[maxn],a[maxn];        int block,belong[maxn],num;        std::vector<int> vc[maxn];        void update(int x) {            vc[x].clear();            for(int i=(x-1)*block+1;i<=x*block;i++)                vc[x].push_back(a[i]);            std::sort(vc[x].begin(),vc[x].end());        }        void modify(int l,int r,int c) {            for(int i=l;i<=std::min(r,belong[l]*block);i++)                a[i]+=c;            update(belong[l]);            if(belong[l]!=belong[r]) {                for(int i=(belong[r]-1)*block+1;i<=r;i++)                    a[i]+=c;                update(belong[r]);              }            for(int i=belong[l]+1;i<belong[r];i++)                delta[i]+=c;        }        int query(int l,int r,int c) {            int ans=0;            for(int i=l;i<=std::min(r,belong[l]*block);i++)                if(a[i]+delta[belong[l]]>=c) ++ans;            if(belong[l]!=belong[r])                for(int i=(belong[r]-1)*block+1;i<=r;i++)                    if(a[i]+delta[belong[r]]>=c) ++ans;            for(int i=belong[l]+1;i<belong[r];i++)                ans+=block-(std::lower_bound(vc[i].begin(),vc[i].end(),c-delta[i])-vc[i].begin());            return ans;        }    public:        int work() {            scanf("%d%d",&n,&q);            block=sqrt((n+2)/3);            for(int i=1;i<=n;i++) {                scanf("%d",a+i);                belong[i]=(i-1)/block+1;                vc[belong[i]].push_back(a[i]);                if(i%block==1) ++num;            }            for(int i=1;i<=num;i++) std::sort(vc[i].begin(),vc[i].end());            while(q--) {                char opt=getchar();                while(opt!=‘A‘ && opt!=‘M‘) opt=getchar();                int lf,rg,c;scanf("%d%d%d",&lf,&rg,&c);                if(opt==‘A‘) printf("%d\n",query(lf,rg,c));                else modify(lf,rg,c);            }            return 0;        }}T;int main() {return T.work();}
例題(二):彈飛綿羊

其實,只要你沒學過CT,這道題還是很有希望做出來的。
記錄兩個資訊:
\(tim[i]\) 和 \(whe[i]\) 表示:需要跳幾次才能出這個塊,出了這個塊會到哪個點上。

每一次修改彈力係數,最多隻會影響本塊內會跳到這個點上的彈簧。

所以每一次修改就重構塊。

於是就歡樂的解決了這道題。

(至今還沒寫對LCT的我就靠這個安慰自己)

#include<bits/stdc++.h>const int maxn=2e5+5;class Divid_Block {    private:        int a[maxn];        int belong[maxn],whe[maxn],tim[maxn];        int block,n,m;        inline int read() {            int x;char ch;while(!isdigit(ch=getchar()));            for(x=ch-‘0‘;isdigit(ch=getchar());x=x*10+ch-‘0‘);            return x;        }        void build() {            block=sqrt((n+2)/3);            for(int i=1;i<=n;i++)                belong[i]=(i-1)/block+1;            belong[n+1]=belong[n]+1;            for(int i=n;i;i--) {                whe[i]=i+a[i];                int r=block*belong[i]>n?n:block*belong[i];                if(whe[i]>r) tim[i]=1;                else tim[i]=tim[whe[i]]+1,whe[i]=whe[whe[i]];            }            return ;        }        void modify(int pos,int val) {            a[pos]=val;            for(int i=block*belong[pos];i>=block*(belong[pos]-1);i--) {                whe[i]=i+a[i];                if(whe[i]>block*belong[pos]) tim[i]=1;                else tim[i]=tim[whe[i]]+1,whe[i]=whe[whe[i]];            }        }        int query(int pos) {            int ans=0;            while(pos<=n) ans+=tim[pos],pos=whe[pos];            return ans;        }    public:        int work() {            n=read();            for(int i=1;i<=n;i++) a[i]=read();            build();            m=read();            while(m--) {                int opt=read(),pos=read();                ++pos;                if(opt-1) {                    int k=read();                    modify(pos,k);                }                else printf("%d\n",query(pos));            }            return 0;        }}T;int main() {return T.work();}
注意事項

關於塊的大小,可以參見初中dalao的部落格(我的分塊他教的)

關於分塊最優塊大小的思考

還有他寫的那個上了洛咕日報的部落格:

淺談基礎根號演算法——分塊

lhy %%% 這個 \((A+C)/2\) 在我們機房裡天天吊虐我。

分塊適用範圍很廣,基本僅次於 \(n^{2}\) 暴力。

走投無路時可以考慮哦。

歸納(三):分塊

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.