CF1446D2 Frequency Problem - 数据结构
题意
给定正整数数列 \(\{a_n\}\),求最长的子区间的长度,使得这个区间内有至少两种出现次数最多的数。
\(1\le n\le 2\times 10^5\)。
题解
来自 lxl 的 \(1\log\) 做法!不过在这个数据范围下并无优势。
定义一个区间是合法的,当且仅当其中有至少两种出现次数最多的数。
首先,找到全局的众数 \(m\),\(m\) 一定是答案区间的两个众数之一。因为如果一个区间内 \(m\) 的出现次数不是最多的,那我们一定可以向外扩充区间,找到更大的一个合法区间。
我们枚举一个 \(x(x\neq m)\),并计算以 \(x,m\) 为众数的最大区间。设 \(x\) 的全局出现次数为 \(c(x)\),若我们能在 \(\mathcal{O}(c(x)\log n)\) 的复杂度内计算这个,问题就解决了。
我们将 \(x\) 的所有出现位置从小到大排序。维护 \(m\) 的出现位置的集合 \(S\),按顺序枚举 \(x\) 的所有出现位置 \(p_i\),并将 \(S\) 中 \(>p_i\) 且最小的元素标记,然后删除。然后我们倒序枚举 \(x\) 的出现位置,对于 \(S\) 中的每个前驱做一遍这个操作。
例如,对于 mxxmm
这个序列,我们会让第一个 \(x\) 标记第二个 \(m\),第二个 \(x\) 标记第一、三个 \(m\)。
这样每个 \(x\) 的出现位置都标记了不超过两个 \(m\),总的个数是 \(\mathcal{O}(c(x))\) 的。对于那些没被标记的 \(m\),我们一定不能让它出现在答案区间中。所以它们充当了分隔符的作用,对于每两个相邻的分隔符之间,计算最长的合法区间即可。
复杂度 \(\mathcal{O}(\sum_x c(x)\log n)=\mathcal{O}(n\log n)\)。
代码
#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;
}