P8330 [ZJOI2022] 众数

Solution#

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

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

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

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

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

对于 sizyT,我们交换 x,y 再算一遍,对于每个 x,枚举其他所有的 y 的位置是 O(n) 的,所以此部分总时间复杂度为 O(n2T)

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

总时间复杂度为 O(nT+n2T),取 T=n 时达到平衡:O(nn)


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;
}
posted @   MrcFrst  阅读(39)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示
主题色彩