CF1446D2 Frequency Problem - 数据结构

题意

给定正整数数列 {an},求最长的子区间的长度,使得这个区间内有至少两种出现次数最多的数。

1n2×105

题解

来自 lxl 的 1log 做法!不过在这个数据范围下并无优势。

定义一个区间是合法的,当且仅当其中有至少两种出现次数最多的数。

首先,找到全局的众数 mm 一定是答案区间的两个众数之一。因为如果一个区间内 m 的出现次数不是最多的,那我们一定可以向外扩充区间,找到更大的一个合法区间。

我们枚举一个 x(xm),并计算以 x,m 为众数的最大区间。设 x 的全局出现次数为 c(x),若我们能在 O(c(x)logn) 的复杂度内计算这个,问题就解决了。

我们将 x 的所有出现位置从小到大排序。维护 m 的出现位置的集合 S,按顺序枚举 x 的所有出现位置 pi,并将 S>pi 且最小的元素标记,然后删除。然后我们倒序枚举 x 的出现位置,对于 S 中的每个前驱做一遍这个操作。

例如,对于 mxxmm 这个序列,我们会让第一个 x 标记第二个 m,第二个 x 标记第一、三个 m

这样每个 x 的出现位置都标记了不超过两个 m,总的个数是 O(c(x)) 的。对于那些没被标记的 m,我们一定不能让它出现在答案区间中。所以它们充当了分隔符的作用,对于每两个相邻的分隔符之间,计算最长的合法区间即可。

复杂度 O(xc(x)logn)=O(nlogn)

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
#define Debug(...) fprintf(stderr,__VA_ARGS__)
typedef long long ll;
const int N=2e5+5;
int n,a[N],cnt[N];
vector<int> occ[N];
struct Point{int p,v;};
bool operator<(const Point &p1,const Point &p2){
	return p1.p<p2.p;
}
bool operator==(const Point &p1,const Point &p2){
	return p1.p==p2.p&&p1.v==p2.v;
}
bool Between(int mode,int x,int y){
	auto it=upper_bound(occ[mode].begin(),occ[mode].end(),x);
	return it!=occ[mode].end()&&*it<y;
}
int val[N*2],tot,tm[N*2];
int main(){
	ios::sync_with_stdio(false),cin.tie(nullptr);
	cin>>n;
	For(i,1,n){
		cin>>a[i];
		++cnt[a[i]],occ[a[i]].push_back(i);
	}
	int mode=max_element(cnt+1,cnt+n+1)-cnt,ans=0;
	set<int> st(occ[mode].begin(),occ[mode].end());
	vector<int> del;
	For(x,1,n){
		if(x==mode||!cnt[x]) continue;
		for(int i:del) st.insert(i);
		del.clear();
		vector<Point> tg;
		for(int i:occ[x]){
			auto it=st.upper_bound(i);
			if(it!=st.end()) tg.push_back({*it,-1}),del.push_back(*it),st.erase(it);
			tg.push_back({i,1});
		}
		for(int i=occ[x].size()-1;i>=0;--i){
			auto it=st.lower_bound(occ[x][i]);
			if(it!=st.begin()) tg.push_back({*--it,-1}),del.push_back(*it),st.erase(it);
		}
		sort(tg.begin(),tg.end());
		tg.erase(unique(tg.begin(),tg.end()),tg.end());
		for(auto i=tg.begin();i!=tg.end();++i){
			auto l=i;
			for(;next(i)!=tg.end()&&!Between(mode,i->p,next(i)->p);++i);
			int lim,rim;
			auto it=lower_bound(occ[mode].begin(),occ[mode].end(),l->p),itr=upper_bound(occ[mode].begin(),occ[mode].end(),i->p);
			if(it==occ[mode].begin()) lim=0;
			else lim=*--it;
			if(itr==occ[mode].end()) rim=n;
			else rim=*itr-1;
			++tot,val[0+n]=lim,tm[0+n]=tot;
			int cur=0;
			for(auto j=l;j<=i;++j){
				cur+=j->v;
				if(tm[cur+n]==tot) ans=max(ans,(j==i?rim:next(j)->p-1)-val[cur+n]);
				else val[cur+n]=j->p,tm[cur+n]=tot;
			}
		}
	}
	cout<<ans<<'\n';
	return 0;
}

作者:alan-zhao-2007

出处:https://www.cnblogs.com/alan-zhao-2007/p/cf1446d2-sol.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Alan_Zhao_2007  阅读(94)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题