• 数列分块入门(最后一个难啊)


    感觉这个不算科技吧,就是现在把操作降在块内了,算是暴力的优化

    https://loj.ac/problem/6277

    LOJ6277. 数列分块入门 1

    对于这些数你有两种操作,0是区间+,1是区间查询

    #include<bits/stdc++.h>
    using namespace std;
    #define N 50005
    int m,belong[N];
    int a[N],tag[N];
    void change(int l,int r,int val)
    {
        for(int i=l;i<=min(r,belong[l]*m);i++) a[i]+=val;//将a块内剩余部分暴力更新
        if(belong[l]!=belong[r])
        {
            for(int i=(belong[r]-1)*m+1;i<=r;i++) a[i]+=val;//将b块内剩余部分暴力更新
        }
        for(int i=belong[l]+1;i<=belong[r]-1;i++)tag[i]+=val;//将整块需要加的值更新上去
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        m=sqrt(n);//一块有几个
        for(int i=1;i<=n;i++) belong[i]=(i-1)/m+1;//将每个值分到所给的块内
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1,op,l,r,val;i<=n;i++)
        {
            scanf("%d%d%d%d",&op,&l,&r,&val);
            if(!op)change(l,r,val);
            else printf("%d
    ",tag[belong[r]]+a[r]);//tag获取这个序列被加的值
        }
        return 0;
    }

    LOJ6278. 数列分块入门 2

    这个还是区间加,但是要查询区间小于c*c的值

    这个就麻烦多了,还要排序的啊

    #include<bits/stdc++.h>
    using namespace std;
    #define N 50005
    int n,m,belong[N];
    int a[N],tag[N];
    vector<int>vec[N];
    void reset(int x)
    {
        //将两个特殊的块更新
        vec[x].clear();
        for(int i=(x-1)*m+1; i<=min(x*m,n); i++)vec[x].push_back(a[i]);
        sort(vec[x].begin(),vec[x].end());
    }
    void change(int l,int r,int val)
    {
        for(int i=l; i<=min(r,belong[l]*m); i++) a[i]+=val; //将a块内剩余部分暴力更新
        reset(belong[l]);
        if(belong[l]!=belong[r])
        {
            for(int i=(belong[r]-1)*m+1; i<=r; i++) a[i]+=val; //将b块内剩余部分暴力更新
            reset(belong[r]);
        }
        for(int i=belong[l]+1; i<=belong[r]-1; i++)tag[i]+=val; //将整块需要加的值更新上去
    }
    int query(int l,int r,int val)
    {
        int ans=0;
        for(int i=l; i<=min(belong[l]*m,r); i++)if(a[i]+tag[belong[l]]<val)ans++;
        if(belong[l]!=belong[r])
            for(int i=(belong[r]-1)*m+1; i<=r; i++)if(a[i]+tag[belong[r]]<val)ans++;
        for(int i=belong[l]+1; i<=belong[r]-1; i++)
            ans+=lower_bound(vec[i].begin(),vec[i].end(),val-tag[i])-vec[i].begin();
        return ans;
    }
    int main()
    {
        scanf("%d",&n);
        m=sqrt(n);//一块有几个
        for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1; //将每个值分到所给的块内
        for(int i=1; i<=n; i++)scanf("%d",&a[i]),vec[belong[i]].push_back(a[i]);
        for(int i=1;i<=belong[n];i++)sort(vec[i].begin(),vec[i].end());//每块要先保证有序,因为以后不再更新了
        for(int i=1,op,l,r,val; i<=n; i++)
        {
            scanf("%d%d%d%d",&op,&l,&r,&val);
            if(!op)change(l,r,val);
            else printf("%d
    ",query(l,r,val*val));
        }
        return 0;
    }

    LOJ6279. 数列分块入门 3

    询问区间内小于某个值 x的前驱(比其小的最大元素)

    这个上面改改就行了

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100005
    int n,m,belong[N];
    int a[N],tag[N];
    vector<int>vec[N];
    void reset(int x)
    {
        //将两个特殊的块更新
        vec[x].clear();
        for(int i=(x-1)*m+1; i<=min(x*m,n); i++)vec[x].push_back(a[i]);
        sort(vec[x].begin(),vec[x].end());
    }
    void change(int l,int r,int val)
    {
        for(int i=l; i<=min(r,belong[l]*m); i++) a[i]+=val; //将a块内剩余部分暴力更新
        reset(belong[l]);
        if(belong[l]!=belong[r])
        {
            for(int i=(belong[r]-1)*m+1; i<=r; i++) a[i]+=val; //将b块内剩余部分暴力更新
            reset(belong[r]);
        }
        for(int i=belong[l]+1; i<=belong[r]-1; i++)tag[i]+=val; //将整块需要加的值更新上去
    }
    int query(int l,int r,int val)
    {
        int ans=-1;
        for(int i=l; i<=min(belong[l]*m,r); i++)if(a[i]+tag[belong[l]]<val)ans=max(ans,a[i]+tag[belong[l]]);
        if(belong[l]!=belong[r])
            for(int i=(belong[r]-1)*m+1; i<=r; i++)if(a[i]+tag[belong[r]]<val)ans=max(ans,a[i]+tag[belong[r]]);
        for(int i=belong[l]+1,pos; i<=belong[r]-1; i++)
        {
            pos=lower_bound(vec[i].begin(),vec[i].end(),val-tag[i])-vec[i].begin();
            if(pos>=1) ans=max(ans,vec[i][pos-1]+tag[i]);
        }
        return ans;
    }
    
    int main()
    {
        scanf("%d",&n);
        m=sqrt(n);//一块有几个
        for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1; //将每个值分到所给的块内
        for(int i=1; i<=n; i++)scanf("%d",&a[i]),vec[belong[i]].push_back(a[i]);
        for(int i=1;i<=belong[n];i++)sort(vec[i].begin(),vec[i].end());//每块要先保证有序,因为以后不更新了
        for(int i=1,op,l,r,val; i<=n; i++)
        {
            scanf("%d%d%d%d",&op,&l,&r,&val);
            if(!op)change(l,r,val);
            else printf("%d
    ",query(l,r,val));
        }
        return 0;
    }

     当然也可以用set去维护这个块,那个lowwerbound不要用错

    #include<stdio.h>
    #include<bits/stdc++.h>
    using namespace std;
    #define N 100005
    int n,m,belong[N];
    int a[N],tag[N];
    set<int>S[N];
    void change(int l,int r,int val)
    {
        for(int i=l; i<=min(r,belong[l]*m); i++)
        {
            S[belong[l]].erase(a[i]);
            a[i]+=val; //将a块内剩余部分暴力更新
            S[belong[l]].insert(a[i]);
        }
        if(belong[l]!=belong[r])
        {
            for(int i=(belong[r]-1)*m+1; i<=r; i++)
            {
                S[belong[r]].erase(a[i]);
                a[i]+=val; //将b块内剩余部分暴力更新
                S[belong[r]].insert(a[i]);
            }
        }
        for(int i=belong[l]+1; i<=belong[r]-1; i++)tag[i]+=val; //将整块需要加的值更新上去
    }
    int query(int l,int r,int val)
    {
        int ans=-1;
        for(int i=l; i<=min(belong[l]*m,r); i++)if(a[i]+tag[belong[l]]<val)ans=max(ans,a[i]+tag[belong[l]]);
        if(belong[l]!=belong[r])
            for(int i=(belong[r]-1)*m+1; i<=r; i++)if(a[i]+tag[belong[r]]<val)ans=max(ans,a[i]+tag[belong[r]]);
        for(int i=belong[l]+1; i<=belong[r]-1; i++)
        {
            set<int>::iterator it=S[i].lower_bound(val-tag[i]);//小于等于
            if(it!=S[i].begin())ans=max(ans,*(--it)+tag[i]);
        }
        return ans;
    }
    int main()
    {
        scanf("%d",&n);
        m=sqrt(n+0.5);//一块有几个
        for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1; //将每个值分到所给的块内
        for(int i=1; i<=n; i++)scanf("%d",&a[i]),S[belong[i]].insert(a[i]);
        for(int i=1,op,l,r,val; i<=n; i++)
        {
            scanf("%d%d%d%d",&op,&l,&r,&val);
            if(!op)change(l,r,val);
            else printf("%d
    ",query(l,r,val));
        }
        return 0;
    }

    LOJ#6280. 数列分块入门 4

    涉及区间求和,第一个代码xjb改改就好

    #include<bits/stdc++.h>
    using namespace std;
    #define N 50005
    #define ll long long
    int m,belong[N];
    ll a[N],sum[N],tag[N];
    void change(int l,int r,int val)
    {
        for(int i=l;i<=min(r,belong[l]*m);i++) a[i]+=val,sum[belong[l]]+=val;//将a块内剩余部分暴力更新
        if(belong[l]!=belong[r])
        {
            for(int i=(belong[r]-1)*m+1;i<=r;i++) a[i]+=val,sum[belong[r]]+=val;;//将b块内剩余部分暴力更新
        }
        for(int i=belong[l]+1;i<=belong[r]-1;i++)tag[i]+=val;//将整块需要加的值更新上去
    }
    ll query(int l,int r)//和上面思路差不多
    {
        ll ans=0;
        for(int i=l;i<=min(r,belong[l]*m);i++)ans+=a[i]+tag[belong[l]];
        if(belong[l]!=belong[r])
            for(int i=(belong[r]-1)*m+1;i<=r;i++)ans+=a[i]+tag[belong[r]];
        for(int i=belong[l]+1;i<=belong[r]-1;i++) ans+=sum[i]+m*tag[i];
        return ans;
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        m=sqrt(n+0.5);//一块有几个
        for(int i=1;i<=n;i++) belong[i]=(i-1)/m+1;//将每个值分到所给的块内
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum[belong[i]]+=a[i];
        for(int i=1,op,l,r,val;i<=n;i++)
        {
            scanf("%d%d%d%d",&op,&l,&r,&val);
            if(!op)change(l,r,val);
            else printf("%d
    ",query(l,r)%(val+1));
        }
        return 0;
    }

    LOJ#6281. 数列分块入门 5

    区间开方,区间求和,怎么那么像呢,这个不是暴力线段树更新?

    当然分块也能做啊,上面的题目也可以线段树刷的

    #include<bits/stdc++.h>
    using namespace std;
    #define N 50005
    #define ll long long
    int m,belong[N];
    int a[N],sum[N],tag[N];
    void la(int x)
    {
        if(tag[x])return;
        tag[x]=1,sum[x]=0;
        for(int i=(x-1)*m+1; i<=x*m; i++)
        {
            a[i]=sqrt(a[i]),sum[x]+=a[i];
            if(a[i]>1) tag[x]=0;
        }
    }
    void change(int l,int r)
    {
        for(int i=l; i<=min(r,belong[l]*m); i++) sum[belong[l]]-=a[i],a[i]=sqrt(a[i]),sum[belong[l]]+=a[i]; //将a块内剩余部分暴力更新
        if(belong[l]!=belong[r])
        {
            for(int i=(belong[r]-1)*m+1; i<=r; i++) sum[belong[r]]-=a[i],a[i]=sqrt(a[i]),sum[belong[r]]+=a[i]; //将b块内剩余部分暴力更新
        }
        for(int i=belong[l]+1; i<=belong[r]-1; i++)la(i);//将整块需要加的值更新上去
    }
    int query(int l,int r)//和上面思路差不多
    {
        int ans=0;
        for(int i=l; i<=min(r,belong[l]*m); i++)ans+=a[i];
        if(belong[l]!=belong[r])
            for(int i=(belong[r]-1)*m+1; i<=r; i++)ans+=a[i];
        for(int i=belong[l]+1; i<=belong[r]-1; i++)ans+=sum[i];
        return ans;
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        m=sqrt(n+0.5);//一块有几个
        for(int i=1; i<=n; i++) belong[i]=(i-1)/m+1; //将每个值分到所给的块内
        for(int i=1; i<=n; i++) scanf("%d",&a[i]),sum[belong[i]]+=a[i];
        for(int i=1,op,l,r,val; i<=n; i++)
        {
            scanf("%d%d%d%d",&op,&l,&r,&val);
            if(!op)change(l,r);
            else printf("%d
    ",query(l,r));
        }
        return 0;
    }

    LOJ#6282. 数列分块入门 6

    给出一个长为n的数列,以及n个操作,操作涉及单点插入,单点询问,数据随机生成。

    vector的insert或者链表都可以吧,但是数据随机生成啊,就可以分块了

    #include<bits/stdc++.h>
    using namespace std;
    #define N 200005
    #define ll long long
    int m,m1;
    int a[N],tag[N];
    vector<int>vec[N];
    pair<int,int>query(int x)
    {
        int now=1;
        while(x>vec[now].size())x-=vec[now].size(),now++;
        return make_pair(now,x-1);
    }
    void rebuild()
    {
        int tot=0;
        for(int i=1; i<=m1; i++)
        {
            for(auto it:vec[i])tag[++tot]=it;
            vec[i].clear();
        }
        int mt=sqrt(tot);
        for(int i=1; i<=tot; i++)
        {
            vec[(i-1)/mt+1].push_back(tag[i]);
        }
        m1=(tot-1)/mt+1;
    }
    
    void insert(int pos,int x)
    {
        pair<int,int>tmp=query(pos);
        vec[tmp.first].insert(vec[tmp.first].begin()+tmp.second,x);
        if(vec[tmp.first].size()>20*m)rebuild();  //重构
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        m=sqrt(n+0.5);//一块有几个
        for(int i=1; i<=n; i++) scanf("%d",&a[i]),vec[(i-1)/m+1].push_back(a[i]);
        m1=(n-1)/m+1;
        for(int i=1,op,l,r,val; i<=n; i++)
        {
            scanf("%d%d%d%d",&op,&l,&r,&val);
            if(!op)insert(l,r);
            else
            {
                pair<int,int>temp=query(r);
                printf("%d
    ",vec[temp.first][temp.second]);
            }
        }
        return 0;
    }

    LOJ#6283. 数列分块入门 7

    有一个区间乘法,还有区间加法,当然这个也可以去暴力更新

    更新的时候要注意下,就是我乘起来的更新了,你乘的时候加法的也需要啊

    #include<bits/stdc++.h>
    using namespace std;
    #define MD 10007
    #define N 100005
    #define ll long long
    int m,n,belong[N];
    int a[N],atag[N],mtag[N];
    void reset(int x)
    {
        for(int i=(x-1)*m+1; i<=min(n,x*m); i++)
            a[i]=(a[i]*mtag[x]+atag[x])%MD;
        atag[x]=0,mtag[x]=1;
    }
    void add(int l,int r,int val)
    {
        reset(belong[l]);
        for(int i=l; i<=min(r,belong[l]*m); i++)a[i]=(a[i]+val)%MD;
        if(belong[l]!=belong[r])
        {
            reset(belong[r]);
            for(int i=(belong[r]-1)*m+1; i<=r; i++)a[i]=(a[i]+val)%MD;
        }
        for(int i=belong[l]+1; i<=belong[r]-1; i++)atag[i]=(atag[i]+val)%MD;
    }
    void mul(int l,int r,int val)
    {
        reset(belong[l]);
        for(int i=l; i<=min(r,belong[l]*m); i++)a[i]=(a[i]*val)%MD;
        if(belong[l]!=belong[r])
        {
            reset(belong[r]);
            for(int i=(belong[r]-1)*m+1; i<=r; i++)a[i]=(a[i]*val)%MD;
        }
        for(int i=belong[l]+1; i<=belong[r]-1; i++)atag[i]=(atag[i]*val)%MD,mtag[i]=(mtag[i]*val)%MD;
    }
    int main()
    {
        scanf("%d",&n);
        m=sqrt(n+0.5);//一块有几个
        for(int i=1; i<=n; i++) scanf("%d",&a[i]),belong[i]=(i-1)/m+1;
        for(int i=1; i<=belong[n]; i++)mtag[i]=1;
        for(int i=1,op,l,r,val; i<=n; i++)
        {
            scanf("%d%d%d%d",&op,&l,&r,&val);
            if(op==0)add(l,r,val);
            else if(op==1)mul(l,r,val);
            else  printf("%d
    ",(a[r]*mtag[belong[r]]+atag[belong[r]])%MD);
        }
        return 0;
    }

    LOJ#6284. 数列分块入门 8

    查询位于 [l,r]的数字有多少个是 c,再把位于 [l,r]的数字都改为 c

    这个题目类似于第五个题目

    每次都是修改区间,所以问题变得简单了起来

    我每次修改一块的几个,那就是一块的复杂度,然后我只需要对这个块就行修改就好了

    对于不完整块,只能把这个tag先更新一遍,再进行求解

    tag==-1表示这个块的值不相同,如果改为c恰好是-1呢,所以这个值最好选一个没出现的值吧

    所以我对分块有一点浅薄的认识,就是如果暴力来做复杂度很高,而且没有好的(或者说有你不会写)数据结构去维护,这个时候分块的话均摊的复杂度就不大,可以通过这种trick去搞这个题目

    #include<bits/stdc++.h>
    using namespace std;
    #define MD 10007
    #define N 100005
    #define ll long long
    int m,n,belong[N];
    int a[N],tag[N];
    void reset(int x)
    {
        if(tag[x]==-1)return;
        for(int i=(x-1)*m+1; i<=min(n,x*m); i++)a[i]=tag[x];
        tag[x]=-1;
    }
    int query(int l,int r,int val)
    {
        int ans=0;
        reset(belong[l]);
        for(int i=l; i<=min(r,belong[l]*m); i++)
        {
            if(a[i]!=val)a[i]=val;
            else ans++;
        }
        if(belong[l]!=belong[r])
        {
            reset(belong[r]);
            for(int i=(belong[r]-1)*m+1; i<=r; i++)
            {
                if(a[i]!=val)a[i]=val;
                else ans++;
            }
        }
        for(int i=belong[l]+1; i<=belong[r]-1; i++)
        {
            if(tag[i]!=-1)
            {
                if(tag[i]!=val) tag[i]=val;
                else ans+=m;
            }
            else
            {
                for(int j=(i-1)*m+1; j<=i*m; j++)
                {
                    if(a[j]!=val) a[j]=val;
                    else ans++;
                }
                tag[i]=val;
            }
        }
        return ans;
    }
    int main()
    {
        scanf("%d",&n);
        m=sqrt(n+0.5);//一块有几个
        memset(tag,-1,sizeof tag);
        for(int i=1; i<=n; i++) scanf("%d",&a[i]),belong[i]=(i-1)/m+1;
        for(int i=1,l,r,val; i<=n; i++)
        {
            scanf("%d%d%d",&l,&r,&val);
            printf("%d
    ",query(l,r,val));
        }
        return 0;
    }

    LOJ#6285. 数列分块入门 9

    (「BZOJ2724」[Violet 6] 蒲公英)

    给出一个长为n的数列,以及n个操作,操作涉及询问区间的最小众数。

    最后一个难爆了,表示根本想不出来

    应该是完整的所有块的众数,和不完整块中出现的数。

    所以我们可以预处理f(i,j)表示第 i 块到第 j 块的众数(枚举 i 开个桶扫一遍)。

    那么只要能快速得出一个数在某个区间内出现次数即可,每次只要比较至多2√n+1个元素的出现次数,这题就解决了。

    由于没有修改,只要离散化以后,给每个数 x 开个vector,按顺序存下 x 出现的位置,每次询问 x 时把区间的左右端点放进对应 vector 二分一下即可。

    根据均值不等式,可以算出分块大小大概是√(n/logn)

    抄黄学长的博客,迷迷糊糊算是看懂了吧,感觉好难

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,tot;
    int v[50005],belong[50005];
    int f[505][505];
    map<int,int>M;
    int val[50005],cnt[50005];
    vector<int>vec[50005];
    void pre(int x)
    {
        memset(cnt,0,sizeof cnt);
        int mx=0,ans=0;
        for(int i=(x-1)*m+1,t; i<=n; i++)
        {
            cnt[v[i]]++,t=belong[i];
            if(cnt[v[i]]>mx||(cnt[v[i]]==mx&&val[v[i]]<val[ans]))ans=v[i],mx=cnt[v[i]];
            f[x][t]=ans;
        }
    }
    int query(int l,int r,int x)
    {
        int t=upper_bound(vec[x].begin(),vec[x].end(),r)-lower_bound(vec[x].begin(),vec[x].end(),l);
        return t;
    }
    int query(int a,int b)
    {
        int ans,mx;
        ans=f[belong[a]+1][belong[b]-1];
        mx=query(a,b,ans);
        for(int i=a,t; i<=min(belong[a]*m,b); i++)
        {
            t=query(a,b,v[i]);
            if(t>mx||(t==mx&&val[v[i]]<val[ans]))ans=v[i],mx=t;
        }
        if(belong[a]!=belong[b])
            for(int i=(belong[b]-1)*m+1,t; i<=b; i++)
            {
                t=query(a,b,v[i]);
                if(t>mx||(t==mx&&val[v[i]]<val[ans]))ans=v[i],mx=t;
            }
        return ans;
    }
    int main()
    {
        scanf("%d",&n);
        m=200;
        //直接给定块大小,这个比较优秀,这个要根据均值不等式求得,在分块中,不同的块大小设置很影响程序的快慢
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&v[i]);
            if(!M[v[i]])M[v[i]]=++tot,val[tot]=v[i];
            v[i]=M[v[i]],vec[v[i]].push_back(i);
        }
        for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1;
        for(int i=1; i<=belong[n]; i++)pre(i);
        for(int i=1,l,r; i<=n; i++)
        {
            scanf("%d%d",&l,&r);
            printf("%d
    ",val[query(l,r)]);
        }
        return 0;
    }
  • 相关阅读:
    Django第一天
    约束条件 表之间的关系
    数据类型
    初始vue
    JQ事件和事件对象
    Jquery的属性操作和DOM操作
    浏览器对象BOM
    Swiper实现全屏视觉差轮播
    Swiper开篇
    JSON
  • 原文地址:https://www.cnblogs.com/BobHuang/p/9632901.html
Copyright © 2020-2023  润新知