zoukankan      html  css  js  c++  java
  • 归纳(三):分块

    何为分块

    优雅的暴力

    思维难度低下,代码难度低下,非常优秀的一种算法(?)。
    实现方法主要是块与块之间 (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
    ",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
    ",query(pos));
                }
                return 0;
            }
    }T;
    
    int main() {return T.work();}
    
    

    注意事项

    关于块的大小,可以参见初中dalao的博客(我的分块他教的)

    关于分块最优块大小的思考

    还有他写的那个上了洛咕日报的博客:

    浅谈基础根号算法——分块

    lhy %%% 这个 ((A+C)/2) 在我们机房里天天吊虐我。

    分块适用范围很广,基本仅次于 (n^{2}) 暴力。

    走投无路时可以考虑哦。

  • 相关阅读:
    setsockopt()使用方法(參数具体说明)
    Hadoop 2.4.0全然分布式平台搭建、配置、安装
    对称加密与非对称加密
    学习模式----观察者模式(3)
    Java实现 蓝桥杯VIP 算法训练 特殊的数字四十
    Java实现 蓝桥杯VIP 算法训练 特殊的数字四十
    Java实现 蓝桥杯VIP 算法训练 特殊的数字四十
    Java实现 蓝桥杯VIP 算法训练 s01串
    Java实现 蓝桥杯VIP 算法训练 s01串
    Java实现 蓝桥杯VIP 算法训练 s01串
  • 原文地址:https://www.cnblogs.com/LoLiK/p/9785798.html
Copyright © 2011-2022 走看看