【ZJOI2022】 众数(根号分治)

题面

🔗

累了😰🤮不想搬了,就贴个链接好了

题解

当我们对某个区间进行操作过后,区间内只有一种值是有用的(成为众数的那个),所以,不妨就把操作定为将某个区间 [ l , r ] [l,r] [l,r] 中的所有 x x x 变为 y y y

某个数 y y y 可以输出,当且仅当存在 [ l , r ] , x [l,r],x [l,r],x 满足 [ l , r ] [l,r] [l,r] x x x 的个数 加上 [ 1 , l ) ∪ ( r , n ] [1,l)\cup(r,n] [1,l)(r,n] y y y 的个数等于众数出现次数的最大值。

因此我们对每个数值进行考虑。由于所有数的出现次数总和等于 n n n,出现次数大于等于 n \sqrt n n 的数值一定不超过 n \sqrt n n 个,我们可以对出现次数进行根号分治。

对于出现次数大于等于 n \sqrt n n 的数值 x x x,每一个都扫一遍序列,令 S x [ i ] S_x[i] Sx[i] 表示前 i i i 个位置出现 x x x 的次数,每个位置处理出 ( S a i [ i ] − S a i [ j ] ) + S x [ j ] (S_{a_i}[i]-S_{a_i}[j])+S_{x}[j] (Sai[i]Sai[j])+Sx[j] 的最大值,再加上 i i i 后面的 x x x 出现次数,对 x x x 产生贡献。

对于出现次数小于 n \sqrt n n 的数值,会比较麻烦,但是由于最终必须要等于最大值才产生贡献,假设众数最大次数为 M M M ,我们就只需要考虑区间 [ l , r ] [l,r] [l,r] 中众数出现次数 ≥ M − n \geq M-\sqrt n Mn 的情况

我们从左往右扫 r r r ,令 L [ i ] L[i] L[i] 表示当前最大的 l l l 满足 [ l , r − 1 ] [l,r-1] [l,r1] 的众数出现次数为 i i i 。每当扫到一个 r r r ,就只需要枚举与 a r a_r ar 同值的前 n \sqrt n n 个位置,来更新 L L L 数组。出现次数小于 n \sqrt n n a r a_r ar,可以直接枚举完所有前面的同值位置 j j j,同时找到最大的 i i i 满足 L [ i ] > j L[i]>j L[i]>j ,用其更新 a r a_r ar 的答案。

时间复杂度 O ( T n n ) O(Tn\sqrt n) O(Tnn )

CODE

需要卡卡常

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma GCC optimize(2)
using namespace std;
#define MAXN 200005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define SQ 448
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int ct[MAXN],CT[MAXN];
int a[MAXN],b[MAXN],id[MAXN],pr[MAXN],lst[MAXN];
int bu[MAXN][SQ+5],as[MAXN],bi[MAXN],rr[MAXN];
vector<int> vc[SQ+5];
inline int Max(int a,int b) {return a>b ? a:b;}
int main() {
    freopen("mode.in","r",stdin);
    freopen("mode.out","w",stdout);
    int T = read();
    while(T --) {
        n = read();
        map<int,int> mp;
        for(int i = 1;i <= n;i ++) a[i] = read(),mp[a[i]] = 1,ct[i] = 0,lst[i] = 0;
        int cn = 0;
        for(auto i = mp.begin();i != mp.end();i ++) i->SE = ++ cn,id[cn] = i->FI;
        for(int i = 1;i <= n;i ++) a[i] = mp[a[i]],ct[a[i]] ++;
        m = 0;
        for(int i = 1;i <= cn;i ++) {
            CT[i] = ct[i]; as[i] = 0; bi[i] = 0;
            if(ct[i] >= SQ) b[++ m] = i,bi[i] = m,vc[m].resize(CT[i]+2);
        }
        if(cn < SQ) {
            for(int i = 1;i <= cn;i ++) {
                if(!bi[i]) {
                    b[++ m] = i; bi[i] = m;
                }
            }
        }
        int ans = 0;
        set<int> as2;
        for(int i = 1;i <= m;i ++) {
            int cal = CT[b[i]];
            for(int j = 1;j <= n;j ++) ct[j] = 0;
            int cnb = 0,mx = 0,mxa = cal;
            for(int j = 1;j <= n;j ++) {
                if(a[j] == b[i]) {
                    mxa = Max(mxa,mx + cal);
                    cnb ++; cal --;
                }
                else {
                    ct[a[j]] = Max(cnb + 1,ct[a[j]] + 1);
                    mx = Max(mx,ct[a[j]]);
                }
            }
            mxa = Max(mxa,mx);
            if(mxa > ans) {
                ans = mxa;
                as2.clear(); as2.insert(b[i]);
            }
            else if(mxa == ans) {
                as2.insert(b[i]);
            }
        }
        for(int i = 1;i <= n;i ++) ct[i] = 0,rr[i] = 0;
        rr[0] = 0x3f3f3f3f;
        int mm = 0;
        if(cn > m) for(int i = 1;i <= n;i ++) {
            ct[a[i]] ++; pr[i] = lst[a[i]]; lst[a[i]] = i;
            if(bi[a[i]]) {
                int x = bi[a[i]];
                vc[x][ct[a[i]]] = i;
                mm = max(ct[a[i]],mm);
                for(int j = 1,k = ct[a[i]];k > 0 && k >= mm-SQ;j ++,k --) {
                    rr[k] = Max(rr[k],vc[x][j]);
                }
            }
            else {
                int p = i,nw = ct[a[i]] - 1,s2 = 0,s3 = max(0,mm-SQ),cal = CT[a[i]] - nw;
                while(p) {
                    while(rr[s2+1] > pr[p]) s2 ++;
                    while(rr[s3+1] > pr[p]) s3 ++;
                    as[a[i]] = Max(as[a[i]],cal + nw + s2);
                    if(rr[s3] > pr[p]) as[a[i]] = Max(as[a[i]],cal + nw + s3);
                    p = pr[p]; nw --;
                }
                p = i; nw = 1;
                while(p) {
                    rr[nw] = Max(rr[nw],p);
                    p = pr[p]; nw ++;
                }
            }
        }
        if(cn > m) for(int i = 1;i <= cn;i ++) {
            if(!bi[i]) {
                pr[n+1] = lst[i];
                int p = n+1,nw = ct[i],s2 = 0,s3 = max(0,mm-SQ);
                while(p) {
                    while(rr[s2+1] > pr[p]) s2 ++;
                    while(rr[s3+1] > pr[p]) s3 ++;
                    as[i] = Max(as[i],nw + s2);
                    if(rr[s3] > pr[p]) as[i] = Max(as[i],nw + s3);
                    p = pr[p]; nw --;
                }
            }
            if(as[i] > ans) {
                ans = as[i];
                as2.clear(); as2.insert(i);
            }
            else if(as[i] == ans) {
                as2.insert(i);
            }
        }
        AIput(ans,'\n');
        for(auto i = as2.begin();i != as2.end();i ++) {
            AIput(id[*i],'\n');
        }
    }
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值