zoukankan      html  css  js  c++  java
  • 【学习笔记】震惊,全机房都会分块了,就我没有

    震惊,蒟蒻 Midoria7 居然在 1949 年终于写完了分块入门,活到爆!


    分块是一种很暴力很优雅的数据结构。秉承散点暴力,大块整体的原则,能以根号级别复杂度优化很多的暴力。

    以下由于都是隔了很长时间写的,所以码风有可能有变。

    数列分块入门 1

    区间加法,单点查询。

    小点暴力修改,大块打标记。(由于我太懒所以把区间修改区间查询复制过来了)

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=5e4+10;
    int n,S;
    int a[maxn];
    
    inline int read(){
        int x=0,fopt=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')fopt=-1;ch=getchar();}
        while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
        return x*fopt;
    }
    
    int sum[maxn],siz[maxn],L[maxn],R[maxn],belong[maxn],tag[maxn];
    inline void Init(){
        S=sqrt(n);
        for(int i=1;i<=n;i++){
            belong[i]=(i-1)/S+1;
            siz[belong[i]]++;
            sum[belong[i]]+=a[i];
            if(!L[belong[i]])L[belong[i]]=i;
            R[belong[i]]=i;
        }
    }
    
    inline void add(int Lu,int Ru,int v){
        if(belong[Lu]==belong[Ru]){
            sum[belong[Lu]]+=(Ru-Lu+1)*v;
            for(int i=Lu;i<=Ru;i++)
                a[i]+=v;
        }else{
            add(Lu,R[belong[Lu]],v);
            add(L[belong[Ru]],Ru,v);
            for(int i=belong[Lu]+1;i<belong[Ru];i++){
                tag[i]+=v;
                sum[i]+=siz[i]*v;
            }
        }
    }
    
    inline int query(int Lu,int Ru){
        int ans=0;
        if(belong[Lu]==belong[Ru]){
            ans+=(Ru-Lu+1)*tag[belong[Lu]];
            for(int i=Lu;i<=Ru;i++)
                ans+=a[i];
        }else{
            ans+=query(Lu,R[belong[Lu]]);
            ans+=query(L[belong[Ru]],Ru);
            for(int i=belong[Lu]+1;i<belong[Ru];i++)
                ans+=sum[i];
        }
        return ans;
    }
    
    int main(){
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        Init();
        for(int i=1;i<=n;i++){
            int opt=read(),l=read(),r=read(),c=read();
            if(opt==0){
                add(l,r,c);
            }else{
                printf("%d
    ",query(r,r));
            }
        }
        return 0;
    }
    

    数列分块入门 2

    区间加法,询问区间内小于某个值的元素个数。

    用归并排序维护块内的单调性,查询直接 lower_bound。散点暴力统计即可。

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=5e4+10;
    int n,S;
    int a[maxn],c[maxn];
    
    inline int read(){
        int x=0,fopt=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')fopt=-1;ch=getchar();}
        while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
        return x*fopt;
    }
    
    inline bool Cmp(int x,int y){
        return a[x]<a[y];
    }
    
    int sum[maxn],siz[maxn],L[maxn],R[maxn],belong[maxn],tag[maxn];
    inline void Init(){
        S=sqrt(n);
        for(int i=1;i<=n;i++){
            belong[i]=(i-1)/S+1;
            siz[belong[i]]++;
            if(!L[belong[i]])L[belong[i]]=i;
            R[belong[i]]=i;
            c[i]=i;
        }
        for(int i=1;i<=belong[n];i++)
            sort(c+L[i],c+R[i]+1,Cmp);
    }
    
    inline void add(int Lu,int Ru,int v){
        if(belong[Lu]==belong[Ru]){
            for(int i=Lu;i<=Ru;i++)
                a[i]+=v;
            vector<int> A,B;
            for(int i=L[belong[Lu]];i<=R[belong[Lu]];i++)
                if(c[i]>=Lu&&c[i]<=Ru)A.push_back(c[i]);
                else B.push_back(c[i]);
            int p1=L[belong[Lu]],p2=0,p3=0;
            while(p2<A.size()&&p3<B.size())
                a[A[p2]]<a[B[p3]]?(c[p1++]=A[p2++]):(c[p1++]=B[p3++]);
            while(p2<A.size())c[p1++]=A[p2++];
            while(p3<B.size())c[p1++]=B[p3++];
        }else{
            add(Lu,R[belong[Lu]],v);
            add(L[belong[Ru]],Ru,v);
            for(int i=belong[Lu]+1;i<belong[Ru];i++){
                tag[i]+=v;
            }
        }
    }
    
    inline int query(int Lu,int Ru,int v){
        int ans=0;
        if(belong[Lu]==belong[Ru]){
            for(int i=Lu;i<=Ru;i++)
                if(a[i]+tag[belong[Lu]]<v)ans++;
        }else{
            ans+=query(Lu,R[belong[Lu]],v);
            ans+=query(L[belong[Ru]],Ru,v);
            for(int i=belong[Lu]+1;i<belong[Ru];i++){
                a[0]=v-tag[i];
                ans+=lower_bound(c+L[i],c+R[i]+1,0,Cmp)-c-L[i];
            }    
        }
        return ans;
    }
    
    signed main(){
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        Init();
        for(int i=1;i<=n;i++){
            int opt=read(),l=read(),r=read(),x=read();
            if(opt==0){
                add(l,r,x);
            }else{
                printf("%lld
    ",query(l,r,x*x));
            }
        }
        return 0;
    }
    

    数列分块入门 3

    区间加法,查找前驱。

    震惊,这不是 set 吗。大块直接 lower_bound,修改就先删掉,再插入即可。散点依然暴力找。

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=1e5+10;
    int n,S;
    int a[maxn],belong[maxn],tag[maxn];
    set<int> tree[1000+10];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    inline void modify(int l,int r,int c){
        for(int i=l;i<=min(belong[l]*S,r);i++){
            tree[belong[l]].erase(a[i]);
            a[i]+=c;
            tree[belong[l]].insert(a[i]);//先删后加
        }
        if(belong[l]!=belong[r]){
            for(int i=(belong[r]-1)*S+1;i<=r;i++){
                tree[belong[r]].erase(a[i]);
                a[i]+=c;
                tree[belong[r]].insert(a[i]);
            }
        }
        for(int i=belong[l]+1;i<belong[r];i++)
            tag[i]+=c;
    }
    
    inline int Findsucc(int l,int r,int c){
        int ans=-1;
        for(int i=l;i<=min(belong[l]*S,r);i++){
            int sum=a[i]+tag[belong[l]];
            if(sum<c)ans=max(ans,sum);
        }
        if(belong[l]!=belong[r]){
            for(int i=(belong[r]-1)*S+1;i<=r;i++){
                int sum=a[i]+tag[belong[r]];
                if(sum<c)ans=max(ans,sum);
            }
        }
        for(int i=belong[l]+1;i<belong[r];i++){
            int temp=c-tag[i];
            set<int>::iterator it=tree[i].lower_bound(temp);
            if(it==tree[i].begin())continue;
            //因为lower_bound会指向大于等于的数,所以前驱就--it即可。
            it--;
            ans=max(ans,*it+tag[i]);
        }
        return ans;
    }
    
    signed main(){
        n=read();S=sqrt(n);
        for(int i=1;i<=n;i++)
            a[i]=read();
        for(int i=1;i<=n;i++){
            belong[i]=(i-1)/S+1;
            tree[belong[i]].insert(a[i]);
        }
        for(int i=1;i<=n;i++){
            int opt=read(),l=read(),r=read(),c=read();
            if(opt==0){
                modify(l,r,c);
            }else{
                printf("%lld
    ",Findsucc(l,r,c));
            }
        }
        return 0;
    }
    

    数列分块入门 4

    区间加法,区间查询。

    纯板子,不知道为什么放在第 4 个。

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=5e4+10;
    int n,S;
    int a[maxn];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int sum[maxn],L[maxn],R[maxn],belong[maxn],siz[maxn],tag[maxn];
    inline void Init(){
        S=sqrt(n);
        for(int i=1;i<=n;i++){
            belong[i]=(i-1)/S+1;
            siz[belong[i]]++;
            sum[belong[i]]+=a[i];
            if(!L[belong[i]])L[belong[i]]=i;
            R[belong[i]]=i;
        }
    }
    
    void add(int s,int t,int v){
        if(belong[s]==belong[t]){
            sum[belong[s]]+=(t-s+1)*v;
            for(int i=s;i<=t;i++)
                a[i]+=v;
        }else{
            add(s,R[belong[s]],v);
            add(L[belong[t]],t,v);
            for(int i=belong[s]+1;i<belong[t];i++){
                tag[i]+=v;
                sum[i]+=siz[i]*v;
            }
        }
    }
    
    int query(int s,int t){
        int ans=0;
        if(belong[s]==belong[t]){
            ans+=tag[belong[s]]*(t-s+1);
            for(int i=s;i<=t;i++)
                ans+=a[i];
        }else{
            ans+=query(s,R[belong[s]]);
            ans+=query(L[belong[t]],t);
            for(int i=belong[s]+1;i<belong[t];i++)
                ans+=sum[i];
        }
        return ans;
    }
    
    signed main(){
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        for(int i=1;i<=n;i++){
            int opt=read(),l=read(),r=read(),c=read();
            if(opt==0){
                add(l,r,c);
            }else{
                printf("%lld
    ",query(l,r)%(c+1));
            }
        }
        return 0;
    }
    

    数列分块入门 5

    区间开方,区间求和。

    开始不板子了。开方并不具有合并性,但是我们发现即使暴力开方 (2^31),在开方五次后都接近 (1) 了。所以当一个块内的值都小于等于 (1) 的时候(因为有向下取整可能出 (0))的时候,开方也没什么意义了。

    所以我们只需要新开一个数组标记一下就好了。

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=5e4+10;
    int n,S;
    int a[maxn];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int belong[maxn],sum[maxn],L[maxn],R[maxn];
    bool vis[maxn];
    inline void Sqrt(int x){
        if(vis[x])return;
        vis[x]=1;sum[x]=0;
        for(int i=L[x];i<=R[x];i++){
            a[i]=sqrt(a[i]);
            sum[x]+=a[i];
            if(a[i]>1)vis[x]=0;
        }
    }
    
    inline void modify(int l,int r){
        if(vis[belong[l]]==0){
            for(int i=l;i<=min(R[belong[l]],r);i++){
                sum[belong[l]]-=a[i];
                a[i]=sqrt(a[i]);
                sum[belong[l]]+=a[i];
            }
            vis[belong[l]]=1;
            for(int i=L[belong[l]];i<=R[belong[l]];i++)
                if(a[i]>1){
                    vis[belong[l]]=0;break;
                }
        }
        if(belong[l]!=belong[r]&&vis[belong[r]]==0){
            for(int i=L[belong[r]];i<=r;i++){
                sum[belong[r]]-=a[i];
                a[i]=sqrt(a[i]);
                sum[belong[r]]+=a[i];
            }
            vis[belong[r]]=1;
            for(int i=L[belong[r]];i<=R[belong[r]];i++)
                if(a[i]>1){
                    vis[belong[r]]=0;break;
                }
        }
        for(int i=belong[l]+1;i<belong[r];i++)
            Sqrt(i);//如果没有全开方完就暴力开方同时更新标记
    }
    
    int query(int l,int r){
        int ans=0;
        for(int i=l;i<=min(R[belong[l]],r);i++)
            ans+=a[i];
        if(belong[l]!=belong[r]){
            for(int i=L[belong[r]];i<=r;i++)
                ans+=a[i];
        }
        for(int i=belong[l]+1;i<belong[r];i++)
            ans+=sum[i];
        return ans;
    }
    
    signed main(){
        n=read();S=sqrt(n);
        for(int i=1;i<=n;i++)
            a[i]=read();
        for(int i=1;i<=n;i++){
            belong[i]=(i-1)/S+1;
            sum[belong[i]]+=a[i];
            if(!L[belong[i]])L[belong[i]]=i;
            R[belong[i]]=i;
        }
        for(int i=1;i<=n;i++){
            int opt=read(),l=read(),r=read(),c=read();
            if(l>r)swap(l,r);
            if(opt==0){
                modify(l,r);
            }else{
                printf("%lld
    ",query(l,r));
            }
        }
        return 0;
    }
    

    双倍经验

    三倍经验

    数列分块入门 6

    单点插入,单点询问。

    震惊,vector 水过,然后 TLE 70 分。

    个人感觉是 9 道题里细节最多的,也是最暴力的

    网上的各种 vector + lower_bound 并没有看懂。所以我直接定义 (a[i][j]) 表示第 (i) 个块中第 (j) 个数,(len[i]) 表示第 (i) 个块的数的个数。

    修改?直接暴力循环找所在的块,直接把 (r) 以后的往后挪(这也太暴力了吧)。

    但是特殊构造的数据会导致某一个块长度变特别大,然后复杂度就 GG 了。

    所以我们发现某一个块长度太大的时候,新建一个 (b[i][j]) 重新分块,然后再把 (b[i][j]) 复制到 (a[i][j]) 里去。

    很黄很暴力

    思想很好懂。难点是边界的处理,需要大力分类讨论。

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=1e3+10;
    const int Mod=10007;
    int n,m,S,pos;
    int a[maxm][maxm],b[maxm][maxm],len[maxn];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    inline void build(){
        int x=0,cnt=m/S,lenth=0;
        if(m%S!=0)cnt++;
        m+=S;pos=0;S=sqrt(m);//注意最后一个块的大小
        for(int i=1;i<=cnt;i++){
            for(int j=1;j<=len[i];j++){
                x++;
                int nowbelong=(x-1)/S+1;
                b[nowbelong][++lenth]=a[i][j];
                a[i][j]=0;
                if(lenth>=S)lenth=0;
            }
        }
        cnt=m/S;
        if(m%S==0)cnt++;
        for(int i=1;i<cnt;i++){
            len[i]=S;
            for(int j=1;j<=len[i];j++)
                a[i][j]=b[i][j];
        }
        if(m%S==0)len[cnt]=S;
        else len[cnt]=m-S*S;//同上
        for(int j=1;j<=len[cnt];j++)
            a[cnt][j]=b[cnt][j];
    }
    
    signed main(){
        n=m=read();S=sqrt(n);
        for(int i=1;i<=n;i++){
            int x=read();
            int nowbelong=(i-1)/S+1;
            a[nowbelong][++len[nowbelong]]=x;
        }
        for(int i=1;i<=n;i++){
            int opt=read(),l=read(),r=read(),c=read();
            if(opt==0){
                pos++;
                int x=0,j;
                for(j=1;j<=S;j++){
                    if(x+len[j]>=l)break;
                    else x+=len[j];//暴力查找
                }
                len[j]++;l-=x;
                for(int i=len[j];i>l;i--)
                    a[j][i]=a[j][i-1];//暴力后移
                a[j][l]=r;
                if(pos>=S)build();//块太大了,直接重构
            }else{
                int x=0,j;
                for(j=1;j<=S;j++){
                    if(x+len[j]>=r)break;
                    else x+=len[j];
                }
                printf("%lld
    ",a[j][r-x]);
            }
        }
        return 0;
    }
    

    数列分块入门 7

    区间乘法,区间加法,单点询问。

    类似线段树的方法,维护两个标记即可。会线段树 2 就会这个。不会有人不会乘法分配律吧不会吧不会吧。

    不同的是暴力维护散点的时候要先将标记下放,否则乘法会出问题(显然)。当标记下放之后,一定要把标记清零(乘法标记变成 1)。

    由于模数较小,导致可能爆负,所以建议读入的时候就模一下。

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=1e5+10;
    const int Mod=10007;
    int n,S;
    int a[maxn];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int belong[maxn],sum[maxn],L[maxn],R[maxn];
    int tagadd[maxn],tagmul[maxn];
    void add(int l,int r,int c){
        for(int i=L[belong[l]];i<=R[belong[l]];i++){
            a[i]=(a[i]*tagmul[belong[l]]%Mod+tagadd[belong[l]]+Mod)%Mod;
            if(l<=i&&i<=r)a[i]=(a[i]+c+Mod)%Mod;
        }
        tagadd[belong[l]]=0;tagmul[belong[l]]=1;
        if(belong[l]!=belong[r]){
            for(int i=L[belong[r]];i<=R[belong[r]];i++){
                a[i]=(a[i]*tagmul[belong[r]]%Mod+tagadd[belong[r]]+Mod)%Mod;
                if(i<=r)a[i]=(a[i]+c+Mod)%Mod;
            }
        }
        tagadd[belong[r]]=0;tagmul[belong[r]]=1;
        for(int i=belong[l]+1;i<belong[r];i++)
            tagadd[i]=(tagadd[i]+c+Mod)%Mod;
    }
    
    void mul(int l,int r,int c){
        for(int i=L[belong[l]];i<=R[belong[l]];i++){
            a[i]=(a[i]*tagmul[belong[l]]%Mod+tagadd[belong[l]]+Mod)%Mod;
            if(l<=i&&i<=r)a[i]=(a[i]*c+Mod)%Mod;
        }
        tagadd[belong[l]]=0;tagmul[belong[l]]=1;
        if(belong[l]!=belong[r]){
            for(int i=L[belong[r]];i<=R[belong[r]];i++){
                a[i]=(a[i]*tagmul[belong[r]]%Mod+tagadd[belong[r]]+Mod)%Mod;
                if(i<=r)a[i]=(a[i]*c+Mod)%Mod;
            }
        }
        tagadd[belong[r]]=0;tagmul[belong[r]]=1;
        for(int i=belong[l]+1;i<belong[r];i++){
            tagadd[i]=(tagadd[i]*c+Mod)%Mod;//加法标记也要乘c
            tagmul[i]=(tagmul[i]*c+Mod)%Mod;
        }
    }
    
    signed main(){
        n=read();S=sqrt(n);
        for(int i=1;i<=n;i++)
            a[i]=read()%Mod;
        for(int i=1;i<=n;i++){
            belong[i]=(i-1)/S+1;
            if(!L[belong[i]])L[belong[i]]=i;
            R[belong[i]]=i;
            tagadd[belong[i]]=0;
            tagmul[belong[i]]=1;
        }
        for(int i=1;i<=n;i++){
            int opt=read(),l=read(),r=read(),c=read()%Mod;
            if(l>r)swap(l,r);
            if(opt==0){
                add(l,r,c);
            }else if(opt==1){
                mul(l,r,c);
            }else{
                printf("%lld
    ",(a[r]*tagmul[belong[r]]%Mod+tagadd[belong[r]])%Mod);
            }
        }
        return 0;
    }
    

    数列分块入门 8

    操作涉及区间询问等于一个数 (c) 的元素,并将这个区间的所有元素改为 (c)

    区间推平,震惊,珂朵莉树

    应该是这几道题里码量最大的(不过如此)。

    我们维护的标记要有以下几种情况:

    块内值不相等(INF),块内值相等但不是 (c)(相等的那个数),块内值相等且都是 (c)(c))。

    注意 hzwer 的标程中第一种情况是 -1,不过我感觉可能 (c=-1),所以就改成 INF 了。

    INF 的话,直接暴力扫一遍统计更新就可。最后还要统计一下是否更新标记。

    第二种情况只需要更新,还是小块暴力改,大块改标记。

    第三种情况直接更新答案了,不用更新。

    大力分类讨论。

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=1e5+10;
    const int INF=0x3f3f3f3f;
    int n,S;
    int a[maxn];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int belong[maxn],L[maxn],R[maxn],v[maxn];
    inline void Solve(int l,int r,int c){
        int ans=0;
        if(belong[l]==belong[r]){
            if(v[belong[l]]==c)ans+=r-l+1;
            else{
                if(v[belong[l]]!=INF){
                    for(int i=L[belong[l]];i<=R[belong[l]];i++)
                        a[i]=v[belong[l]];
                }
                for(int i=l;i<=r;i++)
                    if(a[i]==c)ans++;
                    else a[i]=c;
                bool flag=0;
                for(int i=L[belong[l]];i<=R[belong[l]];i++)
                    if(a[i]!=c){
                        flag=1;break;
                    }
                if(flag)v[belong[l]]=INF;
                else v[belong[l]]=c;
            }
        }else{
            if(v[belong[l]]==c)ans+=R[belong[l]]-l+1;
            else{
                if(v[belong[l]]!=INF){
                    for(int i=L[belong[l]];i<=R[belong[l]];i++)
                        a[i]=v[belong[l]];
                }
                for(int i=l;i<=R[belong[l]];i++)
                    if(a[i]==c)ans++;
                    else a[i]=c;
                bool flag=0;
                for(int i=L[belong[l]];i<=R[belong[l]];i++)
                    if(a[i]!=c){
                        flag=1;break;
                    }
                if(flag)v[belong[l]]=INF;
                else v[belong[l]]=c;
            }
            if(v[belong[r]]==c)ans+=r-L[belong[r]]+1;
            else{
                if(v[belong[r]]!=INF){
                    for(int i=L[belong[r]];i<=R[belong[r]];i++)
                        a[i]=v[belong[r]];
                }
                for(int i=L[belong[r]];i<=r;i++)
                    if(a[i]==c)ans++;
                    else a[i]=c;
                bool flag=0;
                for(int i=L[belong[r]];i<=R[belong[r]];i++)
                    if(a[i]!=c){
                        flag=1;break;
                    }
                if(flag)v[belong[r]]=INF;
                else v[belong[r]]=c;
            }
            for(int i=belong[l]+1;i<belong[r];i++){
                if(v[i]==c)ans+=S;
                else{
                    if(v[i]!=INF)v[i]=c;
                    else{
                        for(int j=L[i];j<=R[i];j++)
                            if(a[j]==c)ans++;
                            else a[j]=c;
                        bool flag=0;
                        for(int j=L[i];j<=R[i];j++)
                            if(a[j]!=c){
                                flag=1;break;
                            }
                        if(flag)v[i]=INF;
                        else v[i]=c;
                    }
                }
            }
        }
        printf("%lld
    ",ans);
    }
    
    signed main(){
        n=read();S=sqrt(n);
        for(int i=1;i<=n;i++){
            a[i]=read();
            belong[i]=(i-1)/S+1;
            if(!L[belong[i]])L[belong[i]]=i;
            R[belong[i]]=i;
            v[i]=INF;
        }
        for(int i=1;i<=n;i++){
            int l=read(),r=read(),c=read();
            Solve(l,r,c);
        }
        return 0;
    }
    

    数列分块入门 9

    求区间最小众数。

    其实这个题我 TLE 92pts,据说离散化后改块长可过,但是这个代码已经能过蒲公英那道题了,就懒得改了。

    首先用 map 离散化,首先预处理一下每个块内的众数,同时我们要把每个数出现的位置 push 到以这个数为下标的 vector 里。这样我们查询某一个区间内这个数的个数,只需要这样即可:

    inline int getcnt(int l,int r,int x){
        return upper_bound(b[x].begin(),b[x].end(),r)-lower_bound(b[x].begin(),b[x].end(),l);
    }
    

    这样散点我们就能暴力统计了。大块直接取 (max)

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=1e5+10;
    int n,m,S;
    int a[maxn];
    
    char buf[1<<20],*p1,*p2;
    #define gc() (p1==p2?(p2=buf+fread(p1=buf,1,1<<20,stdin),p1==p2?EOF:*p1++):*p1++)
    inline int read(){
        int x=0;bool fopt=1;char ch=gc();
        for(;!isdigit(ch);ch=gc())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=gc())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int id;
    int belong[maxn],L[maxn],R[maxn],v[maxn],cnt[maxn];
    unordered_map<int,int> mp;
    vector<int> b[maxn];
    inline void Init(){
        for(int i=1;i<=n;i++){
            belong[i]=(i-1)/S+1;
            if(!L[belong[i]])L[belong[i]]=i;
            R[belong[i]]=i;
            if(mp[a[i]]==0){
                mp[a[i]]=++id;v[id]=a[i];//同时还要记录原来该数是什么
            }
            a[i]=mp[a[i]];
            b[a[i]].push_back(i);//push下标进去
        }
    }
    
    int f[500+10][500+10];//预处理块最小众数
    void Prework(int x){
        memset(cnt,0,sizeof(cnt));
        int Max=0,ans=0;
        for(int i=L[x];i<=n;i++){
            int temp=++cnt[a[i]];
            if(temp>Max||temp==Max&&v[a[i]]<v[ans]){
                Max=temp;
                ans=a[i];
            }
            f[x][belong[i]]=ans;
        }
    }
    
    inline int getcnt(int l,int r,int x){
        return upper_bound(b[x].begin(),b[x].end(),r)-lower_bound(b[x].begin(),b[x].end(),l);
    }
    
    inline int Solve(int l,int r){
        int ans=f[belong[l]+1][belong[r]-1],Max=getcnt(l,r,ans);
        for(int i=l;i<=min(R[belong[l]],r);i++){
            int temp=getcnt(l,r,a[i]);
            if(temp>Max||temp==Max&&v[a[i]]<v[ans]){
                Max=temp;ans=a[i];
            }
        }
        if(belong[l]!=belong[r]){
            for(int i=L[belong[r]];i<=r;i++){
                int temp=getcnt(l,r,a[i]);
                if(temp>Max||temp==Max&&v[a[i]]<v[ans]){
                    Max=temp;ans=a[i];
                }
            }
        }
        return ans;
    }
    
    signed main(){
        n=read();S=sqrt(n);
        for(int i=1;i<=n;i++)
            a[i]=read();
        Init();
        for(int i=1;i<=belong[n];i++)
            Prework(i);
        int last=0;
        for(int i=1;i<=n;i++){
            int l=read(),r=read();
            if(l>r)swap(l,r);
            last=v[Solve(l,r)];
            printf("%lld
    ",last);
        }
        return 0;
    }
    

    双倍经验

    总结:

    暴力就完了。

  • 相关阅读:
    进阶学习项目实战链接
    Django与数据库操作
    xadmin的使用
    Django 常见的异常
    网站 安全 ---- 常见的 web 攻击
    nodejs--vue
    ECMAScript 6 简介
    Django -- DRF 认证流程
    Django -- 缓存
    Django --- celery异步任务与RabbitMQ模块
  • 原文地址:https://www.cnblogs.com/Midoria7/p/13791471.html
Copyright © 2011-2022 走看看