P8330 [ZJOI2022] 众数

发布时间 2024-01-11 19:07:22作者: MrcFrst

Solution

区间加这个操作看起来很阴间,实际上区间加不会改变区间内元素值的相对关系,所以答案就是区间内的众数出现次数加上区间外的众数出现次数。

操作区间两边如果都有值,那么这两个值相等一定是不劣的,因为如果我们希望 \(x\) 为操作区间之外的众数,那么相邻两个 \(x\) 之间的一段要么不选要么全选一定是最优的。

也就是说,当操作区间的左端点 \(l\) 固定之后,右端点的 \(a_r\) 也固定了,于是右端点就有 \(O(siz_{a_l})\) 个取值,其中 \(siz_x\) 表示 \(x\) 在整个序列中的出现次数。

我们发现如果这样暴力枚举右端点,枚举次数仅与 \(siz\) 相关,而对于出现次数,有很经典的根号分治:所有数的出现次数之和为 \(n\),所以出现次数 \(\gt T\) 的数不超过 \(O(\frac{n}{T})\) 个。所以考虑根号分治,设定阈值为 \(T\)

对于 \(siz_x\gt T\),我们先钦定 \(x\) 为操作区间外的答案,然后选择一个 \(y\) \((y\neq x)\) 令其为操作区间内的答案,此时操作区间的两端都是 \(y\) 一定最优,所以只需记录 \(O(siz_y)\) 枚举即可。答案的计算显然是区间内 \(y\) 的出现次数加上区间外 \(x\) 的出现次数,记录 \(x\) 在序列中出现次数的前缀和即可计算。

对于 \(siz_y\le T\),我们交换 \(x,y\) 再算一遍,对于每个 \(x\),枚举其他所有的 \(y\) 的位置是 \(O(n)\) 的,所以此部分总时间复杂度为 \(O(\frac{n^2}{T})\)

于是就只剩下了 \(siz_x\le T\land siz_y\le T\) 的情况:我们可以用上文提到的方法暴力枚举右端点 \(O(nT)\) 次,然后每次求出区间内的众数即可统计答案,总共需要求 \(O(nT)\) 次区间众数,需要做到单次复杂度近似 \(O(1)\),直接做区间众数显然是不太容易做到的,考虑再次利用 \(siz\le T\):我们只需要考虑 \(siz_\le T\) 的数,所以众数出现次数也 \(\le T\),于是可以处理出 \(R_{i,j}\) 表示左端点为 \(j\),最小使得众数出现次数为 \(i\) \((i\le T)\) 的右端点(没有即为 \(n+1\)),对于每个 \(i\) 用双指针处理,时间复杂度 \(O(nT)\)。然后我们在暴力枚举右端点的时候就可以双指针找答案了,对于每个左端点,复杂度为 \(O(T)\),故此部分总时间复杂度为 \(O(nT)\)

总时间复杂度为 \(O(nT+\frac{n^2}{T})\),取 \(T=\sqrt n\) 时达到平衡:\(O(n\sqrt n)\)


Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
#define re register
namespace IO{
    const int sz=1<<20;
    char a[sz+5],b[sz+5],*p1=a,*p2=a,*t=b,p[105];
    inline char gc(){
        return p1==p2?(p2=(p1=a)+fread(a,1,sz,stdin),p1==p2?
        EOF:*p1++):*p1++;
    }
    il int read(){
        int f=1,x=0; char c=gc();
        for(;c<'0'||c>'9';c=gc())if(c=='-') f*=-1;
        for(;c>='0'&&c<='9';c=gc())x=x*10+(c-'0');
        return x*f;
    }
    inline void flush(){fwrite(b,1,t-b,stdout),t=b; }
    inline void pc(char x){*t++=x; if(t-b==sz) flush(); }
    template<class T> void write(T x,char c='\n'){
        if(x==0) pc('0'); int t=0;
        if(x<0) pc('-'),x*=-1;
        for(;x;x/=10) p[++t]=x%10+'0';
        for(;t;--t) pc(p[t]); pc(c);
    }
    struct F{~F(){flush();}}f;
}
using IO::read;
using IO::write;
const int N=2e5+10,T=450;
int Tc,n,a[N],b[N],V;
int R[T+5][N],sum[N],mn[N];
int ans[N],siz[N],cnt[N],pos[N];
vector<int>v[N];
#define pb emplace_back
il void Solve_1(int x,int y){
    re int mn=0,res=-n;
    for(re int i=0;i<siz[y];i++)
        mn=min(mn,i-sum[v[y][i]]),res=max(res,i+1-sum[v[y][i]]-mn);
    ans[x]=max(ans[x],siz[x]+res);
}
il void Solve_2(int x,int y){
    re int mn=0,res=-n;
    for(re int i=0;i<siz[y];i++)
        res=max(res,sum[v[y][i]]-i-mn),mn=min(mn,sum[v[y][i]]-i-1);
    res=max(res,siz[x]-siz[y]-mn);
    ans[y]=max(ans[y],siz[y]+res);
}
il void Solve_gt(int i){
    for(re int j=1;j<=n;j++)sum[j]=sum[j-1]+(a[j]==i);
    for(re int j=1;j<=V;j++)if(i^j)Solve_1(i,j);
    for(re int j=1;j<=V;j++)if(siz[j]<=T)Solve_2(i,j);
}
il void Init_le(int x){
    for(re int i=1;i<=V;i++)cnt[i]=0;
    re int mx=0;
    for(re int i=1;i<=n;i++){
        if(i^1)cnt[a[i-1]]--,mx-=(a[i-1]==a[R[x][i-1]]);
        R[x][i]=R[x][i-1];
        while(R[x][i]<=n&&mx^x)mx=max(mx,++cnt[a[++R[x][i]]]);
    }
}
il void Solve_le(){
    for(re int i=1;i<=V;i++)if(siz[i]<=T)v[i].pb(n+1);
    re int l=0,mx=0;
    for(re int i=1;i<=V;i++)cnt[i]=0;
    for(re int r=2;r<=n;r++)
        ans[a[r]]=max(ans[a[r]],siz[a[r]]-pos[r]+(mx=max(mx,++cnt[a[r-1]])));
    for(l=1;l<n;l++){
        if(siz[a[l]]>T)continue;
        re int res=0;
        for(re int i=pos[l]+1;i<=siz[a[l]];i++){
            re int r=v[a[l]][i];
            while(res<T&&R[res+1][l+1]<r)res++;
            ans[a[l]]=max(ans[a[l]],res+siz[a[l]]-i+pos[l]+1);
        }
    }
}
il void MrcFrst(){
    n=read();
    for(re int i=1;i<=n;i++)b[i]=a[i]=read();
    sort(b+1,b+1+n),V=unique(b+1,b+1+n)-b-1;
    for(re int i=1;i<=V;i++)v[i].clear();
    for(re int i=1;i<=n;i++)
        a[i]=lower_bound(b+1,b+1+V,a[i])-b,pos[i]=v[a[i]].size(),v[a[i]].pb(i);
    for(re int i=1;i<=V;i++)ans[i]=siz[i]=v[i].size();
    for(re int i=1;i<=V;i++)if(siz[i]>T)Solve_gt(i);
    for(re int i=1;i<=T;i++)Init_le(i);
    Solve_le();
    re int res=0;
    for(re int i=1;i<=V;i++)res=max(res,ans[i]);
    write(res);
    for(re int i=1;i<=V;i++)if(ans[i]==res)write(b[i]);
}
int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    Tc=read();
    while(Tc--)MrcFrst();
    return 0;
}