【平衡规划】【CF1447F2】Frequency Problem (Hard Version)

题目

http://codeforces.com/contest/1446/problem/D2

题目大意

给你一个数组,问最长的一个区间使得这个区间的众数不唯一。

n < = 2 × 1 0 5 n<=2\times10^5 n<=2×105
a i < = n a_i<=n ai<=n

easy a i < = 100 a_i<=100 ai<=100

思路

考试的时候没有想出最重要的性质,变得完全不可做

性质:整个序列的众数一定是最优区间的众数之一。

下面我们令 D D D 表示整个序列的众数

证明:考虑反证法,假设答案区间 [ a , b ] [a,b] [a,b] 的众数不是 D D D,那么我们考虑扩展到 [ a − 1 , a − 2 … , b + 1 , b + 2 … ] [a-1,a-2…,b+1,b+2…] [a1,a2,b+1,b+2] 由于 D D D 出现的最多,所以一定会出现个数相等的情况,所以 [ a , b ] [a,b] [a,b] 不是答案区间。

先看 e a s y easy easy ,由于只有100种 a i a_i ai ,所以我们可以单独考虑每一个数,维护最长相等的区间。

h a r d hard hard 考虑根号平衡规划
出现次数大于 n \sqrt n n 直接向easy那样做
出现次数小于 n \sqrt n n 区间段也< n \sqrt n n ,预处理胡暴力即可

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1000005,M=200005;
int n,a[N],cn[N],c[N],c0[N],ans,maxn,p[N];
template<class I>
void Max(I&p,int q)
{
	p=(p>q?p:q);
}
void ins(int x)
{
	--c0[c[x]],++c[x],++c0[c[x]],Max(maxn,c[x]);
}
void del(int x)
{
	--c0[c[x]],--c[x],++c0[c[x]],maxn-=(c0[maxn]==0);
}
int main()
{
	scanf("%d",&n);
	int mx=0;
	for(int i=1; i<=n; i++) scanf("%d",&a[i]),++cn[a[i]],cn[a[i]]>cn[mx]?mx=a[i]:0;
	if(cn[mx]==n) return putchar('0'),0;
	int lim=(cn[mx]<512?cn[mx]:512);
	for(int i=1; i<=n; i++)
		if(i!=mx&&cn[i]>=512)
		{
			for(int j=-n; j<=n; j++) p[j+M]=-1;
			for(int sum=p[M]=0,j=1; j<=n; j++)
				sum+=(a[j]==mx),sum-=(a[j]==i),~p[sum+M]?Max(ans,j-p[sum+M]),0:p[sum+M]=j;
		}
	for(int i=1; i<=lim; i++)
	{
		for(int j=1; j<=n; j++) c[j]=c0[j]=0;
		c0[maxn=0]=n;
		for(int j=1,k=1,x=0; j<=n; j++)
		{
			while(k<=n&&x+(a[k]==mx)<=i) ins(a[k]),x+=(a[k]==mx),++k;
			c0[maxn]>=2?Max(ans,k-j),0:0,del(a[j]),x-=(a[j]==mx);
		}
	}
	printf("%d",ans);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值