[CF1446D1&D2] Frequency Problem

\(\text{Problem}:\)Easy verson Hard verson

\(\text{Solution}:\)

如果整个序列的众数不唯一,则答案显然为 \(n\)。否则记整个序列的众数为 \(p\),有性质:最长的众数不唯一的子区间内一定有出现过 \(p\),且 \(p\) 一定是该区间的众数之一。证明详见 Duyi's tutorial

考虑枚举另一个该区间的众数 \(q\),则只需要求出 \(p,q\) 出现次数相等的最长区间即可。这个问题类似于求有多少个区间的和为 \(0\),利用前缀和相等的性质即可解决。时间复杂度 \(O(n\max\{a_{i}\})\),可以通过 \(D1\)

优化这一过程。考虑区间众数的在区间内的出现次数,这类与某个数出现次数的问题通常可以用根号分治的思想进行解决。如果一个数出现次数大于等于 \(B\),可以用 \(D1\) 的做法解决。则剩余可能成为答案的区间众数的出现次数小于 \(B\),可以暴力枚举这个出现次数,用双指针解决这个问题。时间复杂度 \(O(\cfrac{n^2}{B}+nB)\),当 \(B=\sqrt n\) 时,时间复杂度为 \(O(n\sqrt n)\),可以通过 \(D2\)

\(\text{Code}:\)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std; const int N=400010, B=303;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n,Ans,a[N],book[N],mxd,pp,qz1[N],qz2[N],F[N],g[N],cnt[N];
vector<int> A;
inline void Add(int x)
{
	cnt[g[a[x]]]--;
	g[a[x]]++;
	cnt[g[a[x]]]++;
}
inline void Del(int x)
{
	cnt[g[a[x]]]--;
	g[a[x]]--;
	cnt[g[a[x]]]++;
}
signed main()
{
	n=read();
	for(ri int i=1;i<=n;i++) a[i]=read(), book[a[i]]++, mxd=max(mxd,book[a[i]]);
	for(ri int i=1;i<=n;i++)
	{
		if(book[i]==mxd)
		{
			if(pp) return printf("%lld\n",n)&0;
			pp=i;
		}
		else if(book[i]>=B) A.eb(i);
	}
	for(ri int i=1;i<=n;i++) qz1[i]=qz1[i-1]+(a[i]==pp);
	for(auto i:A)
	{
		for(ri int j=1;j<=n;j++) qz2[j]=qz2[j-1]+(a[j]==i);
		for(ri int j=0;j<=n+n;j++) F[j]=n+1;
		for(ri int j=1;j<=n;j++)
		{
			Ans=max(Ans,j-F[qz2[j]-qz1[j]+n]+1);
			F[qz2[j-1]-qz1[j-1]+n]=min(F[qz2[j-1]-qz1[j-1]+n],j);
		}
	}
	for(ri int i=1;i<B;i++)
	{
		int l=1;
		for(ri int j=1;j<=n;j++)
		{
			Add(j);
			int tt=g[a[j]];
			if(tt>i)
			{
				while(l<j&&a[l]!=a[j]) Del(l), l++;
				Del(l), l++;
			}
			if(cnt[i]>=2) Ans=max(Ans,j-l+1);
		}
		while(l<=n) Del(l), l++;
	}
	printf("%lld\n",Ans);
	return 0;
}
posted @ 2021-02-27 19:44  zkdxl  阅读(74)  评论(0编辑  收藏  举报