1. 程式人生 > 其它 >ZJOI 2022 題目選做

ZJOI 2022 題目選做

眾數

題目描述

點此看題

解法

眾數問題基本上不能 \(poly\ log\),所以我們著重考慮一些根號演算法。

可以對不同顏色的出現次數根號分治,稱出現次數 \(\geq \sqrt n\) 的為大顏色,否則為小顏色。

首先考慮大顏色對其他顏色的貢獻,可以把大顏色打在原來的序列上,做完字首和之後列舉其他顏色,按順序掃描,記錄字首最小值就可以做到 \(O(n)\),這一部分總時間 \(O(n\sqrt n)\)

然後考慮小顏色對其他顏色的貢獻,這時候要抓住關鍵性質:選取的區間眾數應當 \(\leq \sqrt n\),那麼我們外層列舉眾數大小 \(x\),然後雙指標就可以獲得 \(p_r\) 表示右端點 \(r\)

對應最大左端點,使得眾數為 \(x\),得到這個陣列之後我們列舉顏色 \(y\),對於 \(y\) 的每一個點,我們雙指標維護最大的左端點使得兩點間區間眾數為 \(x\),貢獻就便於計算了,這一部分時間複雜度也是 \(O(n\sqrt n)\)

#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int M = 200005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int T,n,m,k,a[M],b[M],c[M],s[M],ans[M],p[M],mi[M];
vector<int> g[M];
void work()
{
    n=read();k=sqrt(n);
    for(int i=1;i<=n;i++)
        a[i]=b[i]=read(),c[i]=ans[i]=0,g[i].clear();
    sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1;
    for(int i=1;i<=m;i++) g[i].push_back(0);
    for(int i=1;i<=n;i++)
    {
        c[a[i]=lower_bound(b+1,b+1+m,a[i])-b]++;
        g[a[i]].push_back(i);
    }
    for(int i=1;i<=m;i++) g[i].push_back(n+1);
    for(int x=1;x<=m;x++) if(c[x]>=k)
    {
        for(int i=1;i<=n;i++)
            s[i]=s[i-1]+(a[i]==x);
        s[n+1]=s[n];
        for(int y=1;y<=m;y++) if(x^y)
        {
            int mn=0;
            for(int i=1;i<g[y].size();i++)
            {
                int nw=s[g[y][i]]-i;
                ans[y]=max(ans[y],nw-mn+1);
                mn=min(mn,nw);
            }
        }
    }
    for(int x=k;x>=1;x--)
    {
        for(int i=1;i<=m;i++) s[i]=0;
        for(int r=1,l=1;r<=n;r++)
        {
            s[a[r]]++;
            while(s[a[r]]>=x) s[a[l]]--,l++;
            p[r]=l;
        }
        for(int y=1;y<=m;y++) if(ans[y]<x)
        {
            vector<int> &t=g[y];
            for(int i=1,j=0,k=0;i<t.size();i++)
            {
                if(p[k=t[i]-1]<2) continue;
                while(j<i && t[j+1]<p[k]-1) j++;
                if(j<i) ans[y]=max(ans[y],x-(i-j-1));
            }
        }
    }
    int mx=0;
    for(int i=1;i<=m;i++) mx=max(mx,ans[i]+c[i]);
    printf("%d\n",mx);
    for(int i=1;i<=m;i++)
        if(ans[i]+c[i]==mx) printf("%d\n",b[i]);
}
signed main()
{
    T=read();
    while(T--) work();
}