【题解】P12598 参数要吉祥

P12598 参数要吉祥

题意

给定长度为 \(n\) 的序列,\(m\) 次询问区间 \([l,r]\),记 \(c(x)\) 为在该区间中出现次数\(x\) 的数的种类数,求出最大的 \(x\times c(x)\)

题解

知识点:莫队,值域分块。

启发:

  • 利用值域分块单点修改来实现莫队 \(O(1)\) 移动指针。

  • 利用莫队指针移动的性质来将值域分块更新复杂度均摊正确。

下文令 \(n,m\) 以及值域同阶,都用 \(n\) 表示。

小清新莫队。

进行莫队算法的过程中,修改的次数是 \(O(n\sqrt{n})\) 的,查询的次数是 \(O(n)\) 的。

首先有一个非常显然的 \(O(n\sqrt{n} \log_2 n)\) 做法,每次移动指针的时候拿 multiset 或者线段树修改,然后查询最值,但这显然不是复杂度最优的做法,他们单次询问可以做到 \(O(\log_2 n)\),但修改也同样是 \(O(\log_2 n)\) 的,和莫队结合没能均摊好时间复杂度。

考虑值域分块,每个块维护块内的最值。

对于每个询问,在指针移动的过程中,如果修改了一个块内的某个值,显然是不能立即更新的,先它所在的块编号扔到一个栈里,等指针完全移动到该询问后统一处理。

这样做时间复杂度是对的,下面进行证明:

首先,有 \(n\) 次询问,莫队算法将这 \(n\) 次询问按左端点分到了 \(\sqrt{n}\) 个长度为 \(\sqrt{n}\) 的块中。

对于一个块内的询问,左端点的活动范围始终在 \(\sqrt{n}\) 内,右端点单调递增,极端情况下活动范围为 \(n\)

所以一个块内的所有询问总共至多造成 \(\sqrt{n}\) 个值域块的更新,\(\sqrt{n}\) 个值域块更新的总复杂度是 \(O(\sqrt{n}\times \sqrt{n})=O(n)\) 的。

而询问总共分了 \(\sqrt{n}\) 个块,所以最坏复杂度也是 \(O(n\sqrt{n})\) 的。

对于其他操作,值域分块的单点修改是 \(O(1)\) 的,查询是 \(O(\sqrt{n})\) 的,综上,用它可以和莫队结合做到 \(O(n\sqrt{n})\) 的复杂度。

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

#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()

#define N 202505
#define C 450

const int lim=2e5;
int n,m,len,bl[N],a[N],ans[N],cnt[N];

namespace ds{
    int len,c,bl[N],cc[N];
    int st[C],ed[C],mx[C];

    inline void init(){
        len=sqrt(lim);
        c=(lim-1)/len+1;

        rep(i,1,c){
            st[i]=ed[i-1]+1;
            ed[i]=i*len;
        }
        ed[c]=lim;

        rep(i,1,c){
            rep(j,st[i],ed[i]){
                bl[j]=i;
            }
        }
    }

    inline void ref(int k){
        mx[k]=*max_element(cc+st[k],cc+ed[k]+1);
    }

    inline int ask(int l,int r){
        int p=bl[l],q=bl[r],ans=0;

        if(p==q){
            rep(i,l,r){
                ans=max(ans,cc[i]);
            }
        }
        else{
            rep(i,l,ed[p]){
                ans=max(ans,cc[i]);
            }

            rep(i,st[q],r){
                ans=max(ans,cc[i]);
            }

            rep(i,p+1,q-1){
                ans=max(ans,mx[i]);
            }
        }

        return ans;
    }
}

using ds::cc;
using ds::ref;
using ds::ask;

struct node{
    int l,r,i;
}b[N];

bitset<C>upd;
vector<int>tmp;

inline void add(int k){
    int &num=cnt[a[k]];

    if(!upd[ds::bl[num]]){
        tmp.pb(ds::bl[num]);
        upd[ds::bl[num]]=1;
    }

    cc[num]-=num;

    num++;

    cc[num]+=num;

    if(!upd[ds::bl[num]]){
        tmp.pb(ds::bl[num]);
        upd[ds::bl[num]]=1;
    }
}

inline void del(int k){
    int &num=cnt[a[k]];

    if(!upd[ds::bl[num]]){
        tmp.pb(ds::bl[num]);
        upd[ds::bl[num]]=1;
    }

    cc[num]-=num;

    num--;

    cc[num]+=num;

    if(!upd[ds::bl[num]]){
        tmp.pb(ds::bl[num]);
        upd[ds::bl[num]]=1;
    }
}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);

    cin>>n>>m;

    len=sqrt(n);
    ds::init();

    int V=0;

    rep(i,1,n){
        cin>>a[i];
        V=max(V,a[i]);
        bl[i]=(i-1)/len+1;
    }

    rep(i,1,m){
        b[i].i=i;
        cin>>b[i].l>>b[i].r;
    }

    sort(b+1,b+1+m,[](node x,node y){
        if(bl[x.l]!=bl[y.l]){
            return x.l<y.l;
        }

        return x.r<y.r;
    });

    int l=1,r=0;
    rep(i,1,m){
        while(l<b[i].l){
            del(l++);
        }
        while(l>b[i].l){
            add(--l);
        }
        while(r<b[i].r){
            add(++r);
        }
        while(r>b[i].r){
            del(r--);
        }

        for(int x:tmp){
            ref(x);
            upd[x]=0;
        }
        tmp.clear();

        ans[b[i].i]=ask(1,V);
    }

    rep(i,1,m){
        cout<<ans[i]<<"\n";
    }

    return 0;
}
posted @ 2025-05-26 17:41  Lucyna_Kushinada  阅读(8)  评论(0)    收藏  举报