loj 数列分块入门 1 ~ 9 做题笔记

前言

做了 hzwer 的分块系列(

%%% hzwer 学长

loj6277 数列分块入门 1

给定数列,区间修改,单点查询。

区间修改可以打永久标记。没什么好说的,毕竟是 1。

#include <bits/stdc++.h>
#define int long long
using namespace std;

inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f^=!(ch^45),ch=getchar();
    while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}
inline void write(int x){
    if(x<0)x=-x,putchar('-');
    if(x>=10)write(x/10);
    putchar(x%10+'0');
}
inline void writeln(int x){write(x);puts("");}

int n;
int a[50005];

int block,num;
int L[500005],R[500005];
int pos[500005];
int sum[500005],add[500005];

void build(){
    block=sqrt(n);
    num=n/block;
    if(n%block)num++;
    for(int i=1;i<=num;i++){
        L[i]=(i-1)*block+1;
        R[i]=i*block;
    }
    R[num]=n;
    for(int i=1;i<=num;i++){
        for(int j=L[i];j<=R[i];j++){
            pos[j]=i;
            sum[i]+=a[j];
        }
    }
}

void update(int l,int r,int k){
    int x=pos[l],y=pos[r];
    if(x==y){
        for(int i=l;i<=r;i++){
            a[i]+=k;
        }
        sum[x]+=k*(r-l+1);
    }else{
        for(int i=x+1;i<=y-1;i++){
            add[i]+=k;
        }
        for(int i=l;i<=R[x];i++){
            a[i]+=k;
            sum[x]+=k;
        }
        for(int i=L[y];i<=r;i++){
            a[i]+=k;
            sum[y]+=k;
        }
    }
}

int query(int l,int r){
    int x=pos[l],y=pos[r];
    int ans=0;
    if(x==y){
        for(int i=l;i<=r;i++)ans+=a[i];
        ans+=add[x]*(r-l+1);
    }
    else{
        for(int i=x+1;i<=y-1;i++){
            ans+=sum[i]+add[i]*(R[i]-L[i]+1);
        }
        for(int i=l;i<=R[x];i++){
            ans+=a[i];
        }
        ans+=add[x]*(R[x]-l+1);
        for(int i=L[y];i<=r;i++){
            ans+=a[i];
        }
        ans+=add[y]*(r-L[y]+1);
    }
    return ans;
}

signed main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    build();
    while(n--){
        int op=read(),l=read(),r=read(),c=read();
        if(op==0)update(l,r,c);
        else writeln(query(r,r));
    }
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}

loj6278 数列分块入门 2

给定数列,区间加,区间查询小于 \(x\) 的个数。

233333333 线段树你做不了了 hhh

区间小于某个数可以二分求解。复制一份辅助数组 \(d\),初值全部等于 \(a\),对每个块内进行排序。

#include <bits/stdc++.h>
#define int long long
using namespace std;

inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f^=!(ch^45),ch=getchar();
    while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}
inline void write(int x){
    if(x<0)x=-x,putchar('-');
    if(x>=10)write(x/10);
    putchar(x%10+'0');
}
inline void writeln(int x){write(x);puts("");}

int n;
int block,num;
int pos[50005];
int sum[50005];
int add[50005];
int L[50005],R[50005];

int a[50005],d[50005];

void build(){
    block=sqrt(n);
    num=n/block;
    if(n%block)num++;
    for(int i=1;i<=num;i++){
        L[i]=(i-1)*block+1;
        R[i]=i*block;
    }
    R[num]=n;
    for(int i=1;i<=n;i++)pos[i]=(i-1)/block+1;
    for(int i=1;i<=num;i++){
        for(int j=L[i];j<=R[i];j++)sum[i]+=a[j];
        sort(d+L[i],d+R[i]+1);
    }
}

void update(int l,int r,int k){
    int x=pos[l],y=pos[r];
    if(x==y){
        for(int i=l;i<=r;i++)a[i]+=k;
        sum[x]+=(r-l+1)*k;
        for(int i=L[x];i<=R[x];i++)d[i]=a[i];
        sort(d+L[x],d+R[x]+1);
    }else{
        for(int i=x+1;i<=y-1;i++)add[i]+=k;
        //------------------
        for(int i=l;i<=R[x];i++)a[i]+=k;
        sum[x]+=(R[x]-l+1)*k;
        for(int i=L[x];i<=R[x];i++)d[i]=a[i];
        sort(d+L[x],d+R[x]+1);
        //------------------
        for(int i=L[y];i<=r;i++)a[i]+=k;
        sum[y]+=(r-L[y]+1)*k;
        for(int i=L[y];i<=R[y];i++)d[i]=a[i];
        sort(d+L[y],d+R[y]+1);
    }

}

int query(int l,int r,int k){
    int ans=0;
    int x=pos[l],y=pos[r];
    if(x==y){
        for(int i=l;i<=r;i++)if(a[i]+add[x]<k)ans++;
    }else{
        for(int i=l;i<=R[x];i++)if(a[i]+add[x]<k)ans++;
        for(int i=L[y];i<=r;i++)if(a[i]+add[y]<k)ans++;
        for(int i=x+1;i<=y-1;i++){
            ans+=lower_bound(d+L[i],d+R[i]+1,k-add[i])-d-L[i];
        }
    }
    return ans;
}

signed main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=d[i]=read();
    build();
    while(n--){
        int op=read(),l=read(),r=read(),c=read();
        if(op==0)update(l,r,c);
        else writeln(query(l,r,c*c));
    }
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}

loj6279 数列分块入门 3

给定数列,区间加,区间前驱。

loj 的分块题就离不开区间加法

前驱可以二分求解。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

const int N = 1e5+10;
int n;
int a[N];
int L[N],R[N],pos[N],add[N],d[N];
int block,num;
void build(){
    block=sqrt(n);
    num=n/block;
    if(n%block)num++;
    for(int i=1;i<=num;i++){
        L[i]=(i-1)*block+1;
        R[i]=i*block;
    }
    R[num]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1;
    }
    for(int i=1;i<=num;i++){
        sort(d+L[i],d+R[i]+1);
    }
}
void update(int l,int r,int k){
    int x=pos[l],y=pos[r];
    if(x==y){
        for(int i=l;i<=r;i++)a[i]+=k;
        for(int i=L[x];i<=R[x];i++)d[i]=a[i];
        sort(d+L[x],d+R[x]+1);
        return;
    }
    //左端零散块
    for(int i=l;i<=R[x];i++)a[i]+=k;
    for(int i=L[x];i<=R[x];i++)d[i]=a[i];
    sort(d+L[x],d+R[x]+1);
    //中间完整块
    for(int i=x+1;i<=y-1;i++)add[i]+=k;
    //右端零散块
    for(int i=L[y];i<=r;i++)a[i]+=k;
    for(int i=L[y];i<=R[y];i++)d[i]=a[i];
    sort(d+L[y],d+R[y]+1);
}
int query(int l,int r,int k){
    int x=pos[l],y=pos[r];
    if(x==y){
        int ans=-0x3f3f3f3f;
        for(int i=l;i<=r;i++){
            if(a[i]+add[x]<k){
                ans=max(ans,a[i]+add[x]);
            }
        }
        return ans==-0x3f3f3f3f?-1:ans;
    }
    int ans=-0x3f3f3f3f;
    for(int i=l;i<=R[x];i++){
        if(a[i]+add[x]<k){
            ans=max(ans,a[i]+add[x]);
        }
    }
    for(int i=L[y];i<=r;i++){
        if(a[i]+add[y]<k){
            ans=max(ans,a[i]+add[y]);
        }
    }
    //完整块
    for(int i=x+1;i<=y-1;i++){
        int cur=lower_bound(d+L[i],d+R[i]+1,k-add[i])-d;
        if(cur==L[i])continue;
        ans=max(ans,d[cur-1]+add[i]);
    }
    return ans==-0x3f3f3f3f?-1:ans;
}

int main(){
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i],d[i]=a[i];
    build();
    while(n--){
        int opt,l,r,c;
        cin>>opt>>l>>r>>c;
        if(opt==0){
            update(l,r,c);
        }else{
            cout<<query(l,r,c)<<endl;
        }
    }
    return 0;
}

loj6280 数列分块入门 4

数列区间加区间求和。

非常正常的一道线段树分块题。

#include <bits/stdc++.h>
#define int long long
using namespace std;

inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f^=!(ch^45),ch=getchar();
    while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}
inline void write(int x){
    if(x<0)x=-x,putchar('-');
    if(x>=10)write(x/10);
    putchar(x%10+'0');
}
inline void writeln(int x){write(x);puts("");}

int n;
int a[50005];

int block,num;
int L[500005],R[500005];
int pos[500005];
int sum[500005],add[500005];

void build(){
    block=sqrt(n);
    num=n/block;
    if(n%block)num++;
    for(int i=1;i<=num;i++){
        L[i]=(i-1)*block+1;
        R[i]=i*block;
    }
    R[num]=n;
    for(int i=1;i<=num;i++){
        for(int j=L[i];j<=R[i];j++){
            pos[j]=i;
            sum[i]+=a[j];
        }
    }
}

void update(int l,int r,int k){
    int x=pos[l],y=pos[r];
    if(x==y){
        for(int i=l;i<=r;i++){
            a[i]+=k;
        }
        sum[x]+=k*(r-l+1);
    }else{
        for(int i=x+1;i<=y-1;i++){
            add[i]+=k;
        }
        for(int i=l;i<=R[x];i++){
            a[i]+=k;
            sum[x]+=k;
        }
        for(int i=L[y];i<=r;i++){
            a[i]+=k;
            sum[y]+=k;
        }
    }
}

int query(int l,int r){
    int x=pos[l],y=pos[r];
    int ans=0;
    if(x==y){
        for(int i=l;i<=r;i++)ans+=a[i];
        ans+=add[x]*(r-l+1);
    }
    else{
        for(int i=x+1;i<=y-1;i++){
            ans+=sum[i]+add[i]*(R[i]-L[i]+1);
        }
        for(int i=l;i<=R[x];i++){
            ans+=a[i];
        }
        ans+=add[x]*(R[x]-l+1);
        for(int i=L[y];i<=r;i++){
            ans+=a[i];
        }
        ans+=add[y]*(r-L[y]+1);
    }
    return ans;
}

signed main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();   
    build();
    while(n--){
        int op=read(),l=read(),r=read(),c=read();
        if(op==0)update(l,r,c);
        else writeln(query(l,r)%(c+1));
    }
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}

loj6281 数列分块入门 5

给定数列,区间开方,区间求和。

\(\forall i\in [1,n], a_i\in[0,2^{31}-1]\),在这个范围内,\(a_i\) 最多开根 \(6\sim 7\) 次就会变为 \(1\),此时再开根是无意义的,所以维护 \(tag_i\) 表示第 \(i\) 块是否都变为 \(1\)\(0\)(反正再开根无意义的数),若 \(tag_i=0\) 暴力操作整块。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

const int N = 5e4 + 10;
int n;
int a[N];
int pos[N],L[N],R[N],sum[N],tag[N];
int block,num;
void build(){
    block=sqrt(n);
    num=n/block;if(n%block)num++;
    for(int i=1;i<=num;i++){
        L[i]=(i-1)*block+1;
        R[i]=i*block;
    }
    R[num]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1;
        sum[pos[i]]+=a[i];
    }
}
void Sqrt(int x){
    if(tag[x])return;
    sum[x]=0;tag[x]=1;
    for(int i=L[x];i<=R[x];i++){
        a[i]=sqrt(a[i]);
        sum[x]+=a[i];
        if(a[i]>1)tag[x]=0;
    }
}
void update(int l,int r){
    int x=pos[l],y=pos[r];
    if(x==y){
        for(int i=l;i<=r;i++){
            sum[x]-=a[i];
            a[i]=sqrt(a[i]);
            sum[x]+=a[i];
        }
        return;
    }
    for(int i=l;i<=R[x];i++){
        sum[x]-=a[i];
        a[i]=sqrt(a[i]);
        sum[x]+=a[i];
    }
    for(int i=x+1;i<=y-1;i++)Sqrt(i);
    for(int i=L[y];i<=r;i++){
        sum[y]-=a[i];
        a[i]=sqrt(a[i]);
        sum[y]+=a[i];
    }
}
int query(int l,int r){
    int x=pos[l],y=pos[r];
    if(x==y){
        int ans=0;
        for(int i=l;i<=r;i++)ans+=a[i];
        return ans;
    }
    int ans=0;
    for(int i=l;i<=R[x];i++)ans+=a[i];
    for(int i=x+1;i<=y-1;i++)ans+=sum[i];
    for(int i=L[y];i<=r;i++)ans+=a[i];
    return ans;
}

int main(){
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    build();
    while(n--){
        int opt,l,r,c;
        cin>>opt>>l>>r>>c;
        if(opt==0){
            update(l,r);
        }else{
            cout<<query(l,r)<<endl;
        }
    }
    return 0;
}

loj6282 数列分块入门 6

数列单点插入单点查询。

恕本人蒟蒻,实在想不出这题如何用分块解答。

看到插入果断选择 splay。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

const int N = 2e5+10;
int n,root;
struct node{
    int son[2],fa,val,siz;
}tr[N];
#define ls(x) (tr[x].son[0])
#define rs(x) (tr[x].son[1])
#define fa(x) (tr[x].fa)
inline void pushup(int x){tr[x].siz=tr[ls(x)].siz+tr[rs(x)].siz+1;}
void rotate(int x){
    int y=fa(x),z=fa(y);
    int k=rs(y)==x;
    tr[z].son[rs(z)==y]=x,fa(x)=z;
    tr[y].son[k]=tr[x].son[k^1],fa(tr[x].son[k^1])=y;
    tr[x].son[k^1]=y,fa(y)=x;
    pushup(y);pushup(x);
}
void splay(int x,int goal){
    while(fa(x)!=goal){
        int y=fa(x),z=fa(y);
        if(z!=goal){
            if((rs(z)==y) ^ (rs(y)==x))rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
    if(!goal)root=x;
}
int kth(int k){
    int x=root;
    while(true){
        if(tr[ls(x)].siz>=k)x=ls(x);
        else if(tr[ls(x)].siz+1>=k)return x;
        else k-=tr[ls(x)].siz+1,x=rs(x);
    }
}
int a[N];
int idx;
int New(int val,int fa){
    tr[++idx].fa=fa;tr[idx].siz=1;tr[idx].val=val;return idx;
}
void build(int &p,int l,int r,int fa){
    if(l>r)return;
    int mid=l+r>>1;
    p=New(a[mid],fa);
    build(ls(p),l,mid-1,p);
    build(rs(p),mid+1,r,p);
    pushup(p);
}
void init(){
    root=New(-0x3f3f3f3f,0);
    rs(root)=New(0x3f3f3f3f,root);
    tr[root].siz=2;
    build(ls(rs(root)),1,n,rs(root));
    pushup(rs(root));pushup(root);
}
void insert(int pos,int val){
    int x=kth(pos-1),y=kth(pos);
    splay(x,0);splay(y,x);
    ls(y)=New(val,y);
    pushup(y);pushup(x);
}

int main(){
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    init();
    while(n--){
        int p,l,r,c;
        cin>>p>>l>>r>>c;
        if(p==0){
            ++l;
            insert(l,r);
        }else{
            ++r;
            splay(kth(r),0);
            cout<<tr[root].val<<endl;
        }
    }
    return 0;
}

loj6283 数列分块入门 7

给定数列,区间乘,区间加,单点查询。

维护两个懒标记,但是这里情况比较特殊,并不是使用永久标记,而是会类似线段树那样下传标记,注意先乘后加,然后正常维护即可。

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
#define mod 10007
using namespace std;

const int N = 1e5+10;
int n;
int a[N];
int pos[N],L[N],R[N],add[N],mul[N];
int block,num;
void build(){
    block=sqrt(n);
    num=n/block;
    if(n%block)num++;
    for(int i=1;i<=num;i++){
        mul[i]=1;
        L[i]=(i-1)*block+1;
        R[i]=i*block;
    }
    R[num]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1;
    }
}
void clear(int x){
    for(int i=L[x];i<=R[x];i++){
        a[i]=a[i]*mul[x]%mod;
        a[i]=(a[i]+add[x])%mod;
    }
    mul[x]=1;add[x]=0;
}
void Mul(int l,int r,int k){
    int x=pos[l],y=pos[r];
    if(x==y){
        clear(x);
        for(int i=l;i<=r;i++){
            a[i]=a[i]*k%mod;
        }
        return;
    }
    clear(x);
    for(int i=l;i<=R[x];i++){
        a[i]=a[i]*k%mod;
    }
    clear(y);
    for(int i=L[y];i<=r;i++){
        a[i]=a[i]*k%mod;
    }
    for(int i=x+1;i<y;i++){
        add[i]=add[i]*k%mod;mul[i]=mul[i]*k%mod;
    }
}
void Add(int l,int r,int k){
    int x=pos[l],y=pos[r];
    if(x==y){
        clear(x);
        for(int i=l;i<=r;i++){
            a[i]=(a[i]+k)%mod;
        }
        return;
    }
    clear(x);
    for(int i=l;i<=R[x];i++){
        a[i]=(a[i]+k)%mod;
    }
    for(int i=x+1;i<y;i++){
        add[i]=(add[i]+k)%mod;
    }
    clear(y);
    for(int i=L[y];i<=r;i++){
        a[i]=(a[i]+k)%mod;
    }
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    build();
    while(n--){
        int opt,l,r,c;
        cin>>opt>>l>>r>>c;
        if(opt==0)Add(l,r,c);
        if(opt==1)Mul(l,r,c);
        if(opt==2)cout<<(a[r]*mul[pos[r]]%mod+add[pos[r]])%mod<<endl;
    }
    return 0;
}

loj6284 数列分块入门 8

给定数列,求区间 \(=c\) 个数并覆盖为 \(c\)

同样采用下放标记的方式。

在查询,如果发现第 \(i\) 已经覆盖成 \(c\),就把整块加上去,如果没有打过标记就暴力统计。

#include <bits/stdc++.h>
#define endl '\n'
#define inf 0x3f3f3f3f
using namespace std;

const int N = 1e5 + 10;
int a[N],n;
int L[N],R[N],pos[N];
int cov[N];
int block,num;
void build(){
    block=sqrt(n);
    num=n/block;if(n%block)num++;
    for(int i=1;i<=num;i++){
        L[i]=(i-1)*block+1;
        R[i]=i*block;
        cov[i]=inf;
    }
    R[num]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1;
    }
}
void clear(int x){
    if(cov[x]==inf)return;
    for(int i=L[x];i<=R[x];i++){
        a[i]=cov[x];
    }
    cov[x]=inf;
}
int query(int l,int r,int c){
    int x=pos[l],y=pos[r];
    int ans=0;
    if(x==y){
        clear(x);
        for(int i=l;i<=r;i++){
            ans+=a[i]==c;a[i]=c;
        }
        return ans;
    }
    clear(x);
    for(int i=l;i<=R[x];i++){
        ans+=a[i]==c;a[i]=c;
    }
    clear(y);
    for(int i=L[y];i<=r;i++){
        ans+=a[i]==c;a[i]=c;
    }
    for(int i=x+1;i<y;i++){
        if(cov[i]==c)ans+=R[i]-L[i]+1;
        if(cov[i]==inf){
            for(int j=L[i];j<=R[i];j++){
                ans+=a[j]==c;
            }
        }
        cov[i]=c;
    }
    return ans;
}

int main(){
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    build();
    while(n--){
        int l,r,c;
        cin>>l>>r>>c;
        cout<<query(l,r,c)<<endl;
    }
    return 0;
}

loj6285 数列分块入门 9*

Boss 来了。

给定数列,求区间最小众数。

回忆一下我们平时是怎么算众数的,我们开桶 \(t\) 记录每个数出现的次数,最后扫一遍 \(t\) 即可。

分块预处理

看一下题目的数据范围:\(-2^{31}\le\operatorname{others}\le2^{31}-1\)(也就是 int 的范围),这么大,用数组会炸掉,而如果用 std::map,会给复杂度再加上 \(\log\),显然不优(更多详细原因阅读脚注)[1],所以需要离散化,这里使用 unique + lower_bound 的离散化方法。

这样得到的 \(a\) 数组是原 \(b\) 数组中对应的数的下标,输出时须留意。

离散化过后,我们就可以预处理出前缀和数组 \(s_{i,j}\) 表示前 \(i\) 块中 \(j\) 出现的次数。

对于两端零散的块,我们暴力统计;对于中间完整的块,就需要预处理了。

我们预处理出 \(f_{i,j}\) 表示第 \(i\) 块到第 \(j\) 块的众数。

区间查询众数

看一张图来理解:

此时数列已经分成 \(\sqrt{n} \ (n=10)\) 块,已用不同颜色标出。

我们查找 \(l=2,r=7\),显然我们早已求出 \(f\),现在就是要处理两端绿色部分(零散部分),两端的部分出现次数怎么算呢?先用一个桶统计,然后还得加上中间块(橙色部分)的出现次数,用 \(s\) 前缀和可以得到。

一点优化

注:此处设 \(pos_i\) 为第 \(i\) 个数属于第几块。

可以发现,当 \(|pos_r-pos_l|\le 1\) 时,对于中间块的操作,使用 \(f\) 数组查询跟我们暴力处理没有区别,\(s\) 数组统计中间块的出现次数,因为只有 \(1\) 块,所以优化效果微乎其微。

所以当 \(|pos_r-pos_l|\le1\) 我们就打暴力,否则就分块处理。

说句闲话

表示每块起始的数组名从 \(L,R\) 改成了 \(st,ed\),因为觉得按 Shift 麻烦。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

const int N = 1e5 + 10;
int n;
int a[N],b[N];
int st[N],ed[N],pos[N];
int f[318][318];
int s[318][N];
int block,num;
int m[N];

int main(){
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>b[i],a[i]=b[i];
    sort(b+1,b+n+1);
    int len=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(b+1,b+len+1,a[i])-b;
    }
    block=sqrt(n);num=n/block;if(n%block)num++;
    for(int i=1;i<=num;i++){
        st[i]=(i-1)*block+1;
        ed[i]=i*block;
    }
    for(int i=1;i<=n;i++)pos[i]=(i-1)/block+1;
    for(int i=1;i<=num;i++){
        for(int j=st[i];j<=ed[i];j++)s[i][a[j]]++;
        for(int j=1;j<=len;j++)s[i][j]+=s[i-1][j];
    }
    for(int i=1;i<=num;i++){
        for(int j=i;j<=num;j++){
            int mx=f[i][j-1];
            for(int k=st[j];k<=ed[j];k++){
                if(s[j][a[k]]-s[i-1][a[k]]>=s[j][mx]-s[i-1][mx]){
                    if(s[j][a[k]]-s[i-1][a[k]]==s[j][mx]-s[i-1][mx]){
                        mx=min(mx,a[k]);
                    }
                    else mx=a[k];
                }
            }
            f[i][j]=mx;
        }
    }
    while(n--){
        int l,r;cin>>l>>r;
        int x=pos[l],y=pos[r];
        int ans=0;
        if(y-x<=1){
            for(int i=l;i<=r;i++){
                m[a[i]]++;
                if(m[a[i]]>=m[ans]){
                    if(m[a[i]]==m[ans])ans=min(ans,a[i]);
                    else ans=a[i];
                }
            }
            for(int i=l;i<=r;i++)m[a[i]]=0;
        }else{
            ans=f[x+1][y-1];
            for(int i=l;i<=ed[x];i++)m[a[i]]++;
            for(int i=st[y];i<=r;i++)m[a[i]]++;
            for(int i=l;i<=ed[x];i++){
                if(m[a[i]]+s[y-1][a[i]]-s[x][a[i]]>=m[ans]+s[y-1][ans]-s[x][ans]){
                    if(m[a[i]]+s[y-1][a[i]]-s[x][a[i]]==m[ans]+s[y-1][ans]-s[x][ans])ans=min(ans,a[i]);
                    else ans=a[i];
                }
            }
            for(int i=st[y];i<=r;i++){
                if(m[a[i]]+s[y-1][a[i]]-s[x][a[i]]>=m[ans]+s[y-1][ans]-s[x][ans]){
                    if(m[a[i]]+s[y-1][a[i]]-s[x][a[i]]==m[ans]+s[y-1][ans]-s[x][ans])ans=min(ans,a[i]);
                    else ans=a[i];
                }
            }
            for(int i=l;i<=ed[x];i++)m[a[i]]=0;
            for(int i=st[y];i<=r;i++)m[a[i]]=0;
        }
        cout<<b[ans]<<endl;
    }
    return 0;
}

  1. 其实用 std::map 当桶处理大数据也可以,带个 \(\log\) 其实也不会超时,麻烦就麻烦在数组 \(s\) 的处理上,因为按照值域来更新,不离散化值域过大,这里才是超时的原因,所以 std::map 寄了。 ↩︎

posted @ 2022-04-25 21:12  tmjyh09  阅读(42)  评论(0编辑  收藏  举报