[Spark] 古老系列回归之—— [ZJOI2022] 众数
[Spark] 古老系列回归之——[ZJOI2022] 众数
我觉得值得评黑,至少比 [Ynoi 2012] 惊惶的 SCOI2016 难……
一般与颜色出现次数相关的问题,可以考虑根号分治。况且众数的问题可能很多还是不能 Polylog 的。
题目可以转化成求一个区间 \([l,r]\) 最大化区间内众数出现次数与区间外众数出现次数的和。
首先我们考虑设定阈值 \(B\),称出现次数不大于 \(B\) 的颜色是小颜色,否则称为大颜色,一个颜色 \(x\) 的出现次数记作 \(cnt_x\)。分类讨论进行贡献:
Part 1. 存在一层的众数是大颜色
大颜色的数量是有限的,我们可以枚举它,并接受单次时间 \(\mathcal O(n)\) 的处理,考虑枚举外层的颜色是 \(x\),内层的颜色是 \(y\),那么答案就是外层的 \(x\) 出现次数加上内层 \(y\) 的出现次数的最大值,先把 \(x\) 的出现次数计入答案,然后给数列上 \(x\) 的位置赋权值 \(-1\),\(y\) 的位置赋权值为 \(1\),那么问题就变成了求最大字段和。
而我们有一个发现,那就是最优方案中一定存在一种满足 \(a_l=y,a_r=y\),如果不是的话,向内收缩到左右端点权值都是 \(1\) 的情况显然更优,所以我们可以用 \(\mathcal O(cnt_y)\) 的复杂度来计算 \(y\) 对 \(x\) 的可能贡献。我们 \(w_x\)。
内层的众数是大颜色是类似的,但是此时的区间有可能从 1 开始或者以 n 为结尾,我们之后再特殊处理一下分成两段的即可。
Part 2. 两层的众数都是小颜色
我们直接枚举外层是小颜色 \(x\),此时我们需要找到若干区间用区间内众数的出现次数加外层 \(x\) 的出现次数去更新答案,与 Part 1 差不多,我们发现区间的左右端点的两侧一定是 \(x\)(或者在边界上,这个和 Part 1 结尾的特判可以放在一起处理,我们发现这两者的另一个端点都是对应颜色的左侧或右侧一个点,可以一起处理掉)。我们发现,这种区间的个数是 \(cnt_x^2\) 个的,假设所有颜色都是小颜色那么一个点最多和 \(\mathcal O(B)\) 个同色左端点配成一个区间,所以,区间总数量是 \(\mathcal O(nB)\) 的。
然后我们就要考虑如何做 \(\mathcal O(nB)\) 的区间众数查询……
我觉得这一坨可以单独出成题……
这一坨的处理我没看题解,感觉题解的方法需要一定注意力,比较难想到,虽然感觉这个方法也半斤八两,但是肯定还是好一些的。注意以下我们全都是只考虑小颜色。
我们先把询问挂在右端点上。然后对于每个右端点,如果它是小颜色,就处理出这种颜色在它之前的出现,这个能够 \(\mathcal O(nB)\) 地做。然后从大到小,即 \(B\) 到 \(1\) 枚举所有可能的答案 \(k\),从左往右扫描序列,并维护一个 \(mx\) 表示若当前在 \(i\),那么左端点在 \(1\sim mx\),右端点在 \(i\sim n\) 的询问的答案都至少是 \(k\),对于一个确定的右端点,其答案关于左端点单调不增,因此是对的。这个 \(mx\) 就等于 \(1\sim i\) 中所有右端点的左边第 \(k\) 个同色位置的最大值。每个询问都只会被处理一次,一边回答询问一边更新答案即可。
Part 3. 代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int Tsk;
int n,a[N];
struct DiscreteUtil{
int disc[N],btot;
void clear(){btot=0;}
void push(int x){disc[++btot]=x;}
void discrete(){
sort(disc+1,disc+1+btot);
btot=unique(disc+1,disc+1+btot)-disc-1;
}
int query(int x){return lower_bound(disc+1,disc+1+btot,x)-disc;}
}dc;
int cnt[N],sum[N],w[N];
const int B=450;
vector<int> pos[N];
void Work1(){
for(int i=1;i<=dc.btot;i++){
if(cnt[i]<=B) continue;
for(int j=1;j<=n;j++) sum[j]=sum[j-1]+(a[j]==i);
for(int j=1;j<=dc.btot;j++){//里面的
int c=0,ans=0,lst=0;
for(int p:pos[j])
c=max(c+1-(sum[p]-sum[lst]),1),ans=max(ans,c),lst=p;
w[i]=max(w[i],cnt[i]+ans);
}//Case 1
for(int j=1;j<=dc.btot;j++){//外面的
int c=0,ans=0,lst=0;
for(int p:pos[j]){
ans=max(ans,c+(sum[p]-sum[lst]));
c=max(c+(sum[p]-sum[lst])-1,0),lst=p;
}
w[j]=max(w[j],cnt[j]+ans);
}//Case 2
}
}
vector<int> qry[N];int pmd[N],smd[N],tmp[N];
vector<int> lst[N];int scnt[N];
void Prework(){
for(int i=0;i<=n+3;i++) tmp[i]=cnt[i]=sum[i]=w[i]=scnt[i]=pmd[i]=smd[i]=0,qry[i].clear(),lst[i].clear(),pos[i].clear();
for(int i=1;i<=n;i++) dc.push(a[i]);
dc.discrete();
for(int i=1;i<=n;i++) a[i]=dc.query(a[i]);
for(int i=1;i<=n;i++){
pos[a[i]].push_back(i);cnt[a[i]]++;
}
for(int i=1;i<=n;i++){
if(cnt[a[i]]<=B){
if(tmp[a[i]]) lst[i].push_back(tmp[a[i]]);
for(int j:lst[tmp[a[i]]]) lst[i].push_back(j);
}
tmp[a[i]]=i;
}
}
void Work2(){
for(int i=0;i<=n;i++) qry[i].clear();
for(int i=1;i<=n;i++){
if(cnt[a[i]]>B) goto nxt;
for(int j:lst[i])
if(j+1<=i-1) qry[i-1].push_back(j+1);
nxt:;
if(lst[i].size()) scnt[i]=scnt[lst[i][0]]+1;
else scnt[i]=1;
}//较远的插在后面了
for(int i=B;i>=1;i--){
int mx=0;
for(int j=1;j<=n;j++){
if(cnt[a[i]]<=B){
if(i==1) mx=max(mx,j);
else if(lst[j].size()>=i-1) mx=max(lst[j][i-2],mx);
}
while(!qry[j].empty()){
if(qry[j].back()>mx) break;
int k=qry[j].back();qry[j].pop_back();
w[a[j+1]]=max(w[a[j+1]],cnt[a[j+1]]-(scnt[j+1]-scnt[k-1]-1)+i);
}
}
}
}
void Work3(){//枚举外层数,预处理后缀/前缀众数
for(int i=0;i<=dc.btot+1;i++) tmp[i]=0;
for(int i=1;i<=n;i++) tmp[a[i]]++,pmd[i]=max(tmp[a[i]],pmd[i-1]);
for(int i=0;i<=dc.btot+1;i++) tmp[i]=0;
for(int i=n;i>=1;i--) tmp[a[i]]++,smd[i]=max(tmp[a[i]],smd[i+1]);
for(int i=1;i<=dc.btot;i++){
int c=0;
for(int j:pos[i]){
w[i]=max(w[i],pmd[j-1]+cnt[i]-c);
c++;
w[i]=max(w[i],c+smd[j+1]);
}
}
}
int main(){
ios::sync_with_stdio(false),cin.tie(nullptr);
cin>>Tsk;
while(Tsk--){
dc.clear();
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
Prework(),Work1(),Work2(),Work3();
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,w[i]);
cout<<ans<<"\n";
for(int i=1;i<=dc.btot;i++) if(w[i]==ans) cout<<dc.disc[i]<<"\n";
}
}
本文来自博客园,作者:haozexu,转载请注明原文链接:https://www.cnblogs.com/haozexu/p/18914437

浙公网安备 33010602011771号