【学习笔记】震惊,全机房都会分块了,就我没有

震惊,蒟蒻 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\n",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\n",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\n",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\n",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\n",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\n",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\n",(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\n",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\)

UPD:经过神犇 RubyonlyK 的测试,最佳块长是 30。

#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[5000+10][5000+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=30;
    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\n",last);
    }
    return 0;
}

双倍经验

总结:

暴力就完了。

posted @ 2020-10-10 11:21  Midoria7  阅读(127)  评论(2编辑  收藏  举报