题解 [ZJOI2022] 众数
浙江刚考完就知道 Day 1 T2 考了个根号分治
然后今天对着题死活不知道怎么分……
首先容易想到一个 \(O(n*本质不同数字个数^2)\) 的做法
枚举一对 \(i, j\),前缀和维护最小转移点可以找到一个区间内取 j,区间外取 i 的最大出现次数
然后仔细想一下发现只有 \(i出现次数+j出现次数\) 个点是有用的
固定 \(i\),枚举所有 \(j\),那么可以对颜色 \(i\) 维护前缀和来查询任意一个区间内颜色 \(i\) 的出现次数
那么发现只有 \(j\ 出现次数\) 个点是有用的了
这样对一个 \(i\) 处理所有 \(j\) 的复杂度就是 \(O(n)\) 的
那么这时就可以根号分治了,对所有 \(cnt_i>\sqrt n\) 的颜色 \(i\) 这么干
发现这样就处理掉了所有 大-大,大-小 之间的贡献,那么只需要处理 小-小
发现因为剩下的数出现次数都 \(\leqslant \sqrt n\),那么对每种数 \(出现次数^2\) 枚举区间是 \(O(n\sqrt n)\) 的
现在需要能够 \(O(1)\) 查询区间众数出现次数
这个东西本身是不可做的,但是发现我们只需要考虑出现次数 \(\leqslant \sqrt n\) 的数
一个神奇的做法是扫描线,在每个右端点维护到所有左端点的区间众数的出现次数
那么有性质是 \(tim\) 数组单调不升且 \(\forall i,tim_i\leqslant \sqrt n\),这为暴力维护提供了复杂度保证
在每个 \(r\),暴力跳 \(col_r\) 的前一次出现位置并更新那个位置的 \(tim\)
如果更新后不满足单调不升了就大力向前 chkmax
因为每次 chkmax 都会使那个位置的 tim 增大且 tim 的总和是 \(O(n\sqrt n)\) 的所以复杂度正确
这样下来整体就是 \(O(n\sqrt n)\) 的了
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
int a[N];
namespace force{
vector<int> res;
map<int, int> mp;
int uni[N], cnt[N], usiz, ans;
void calc(int now) {
int tot=mp[now], sum, maxn, rec=0;
for (int i=1; i<=n; ++i) {
sum=tot; maxn=0;
for (int j=i; j<=n; ++j) {
if (a[j]==now) --sum;
else if (++cnt[a[j]]>maxn) ++sum, ++maxn;
rec=max(rec, sum);
}
for (int j=i; j<=n; ++j) if (a[j]!=now) --cnt[a[j]];
}
if (rec>ans) ans=rec, res.clear(), res.pb(uni[now]);
else if (rec==ans) res.pb(uni[now]);
}
void solve() {
usiz=ans=0; res.clear(); mp.clear();
for (int i=1; i<=n; ++i) uni[++usiz]=a[i];
sort(uni+1, uni+usiz+1);
usiz=unique(uni+1, uni+usiz+1)-uni-1;
for (int i=1; i<=n; ++i) ++mp[a[i]=lower_bound(uni+1, uni+usiz+1, a[i])-uni];
for (int i=1; i<=usiz; ++i) calc(i);
printf("%d\n", ans);
for (auto it:res) printf("%d\n", it);
}
}
namespace task1{
vector<int> res;
map<int, int> mp;
int uni[N], cnt[N], pre[N], usiz, ans;
void calc(int x, int y) {
int tot=mp[x], minn=0, maxn=0;
for (int i=1; i<=n; ++i) {
if (a[i]==x) pre[i]=pre[i-1]-1;
else if (a[i]==y) pre[i]=pre[i-1]+1;
else pre[i]=pre[i-1];
maxn=max(maxn, pre[i]-minn);
minn=min(minn, pre[i]);
}
tot+=maxn;
if (tot>ans) ans=tot, res.clear(), res.pb(uni[x]);
else if (tot==ans) res.pb(uni[x]);
}
void solve() {
usiz=ans=0; res.clear(); mp.clear();
for (int i=1; i<=n; ++i) uni[++usiz]=a[i];
sort(uni+1, uni+usiz+1);
usiz=unique(uni+1, uni+usiz+1)-uni-1;
for (int i=1; i<=n; ++i) ++mp[a[i]=lower_bound(uni+1, uni+usiz+1, a[i])-uni];
for (int i=1; i<=usiz; ++i) for (int j=1; j<=usiz; ++j) calc(i, j);
printf("%d\n", ans);
sort(res.begin(), res.end());
res.erase(unique(res.begin(), res.end()), res.end());
for (auto it:res) printf("%d\n", it);
}
}
namespace task{
struct qes{int l, r, id, dlt;};
vector<qes> que[N];
vector<int> res, pos[N], tem;
int uni[N], mp[N], cnt[N], pre[N], val[N], lst[N], buc[N], usiz, ans, sqr;
inline int qcnt(int l, int r) {return cnt[r]-(l?cnt[l-1]:0);}
void solve1(int x, int y) {
// cout<<"solve: "<<x<<' '<<y<<endl;
for (int i=1; i<=pos[y].size(); ++i) pre[i]=i-cnt[pos[y][i-1]];
int minn=INF, maxn=0;
// cout<<"pos: "; for (auto it:pos[y]) cout<<it<<' '; cout<<endl;
for (int i=1; i<=pos[y].size(); ++i) {
// cout<<"i: "<<i<<endl;
minn=min(minn, pre[i]-1);
// cout<<"minn: "<<minn<<endl;
// cout<<"pre: "<<pre[i]<<endl;
maxn=max(maxn, mp[x]+pre[i]-minn);
}
// cout<<"maxn: "<<maxn<<endl;
if (maxn>ans) ans=maxn, res.clear(), res.pb(uni[x]);
else if (maxn==ans) res.pb(uni[x]);
// exit(0);
}
void solve2(int x, int y) {
// cout<<"solve2: "<<x<<' '<<y<<endl;
for (int i=1; i<=pos[y].size(); ++i) pre[i]=cnt[pos[y][i-1]]-i;
int minn=INF, maxn=0;
for (int i=1; i<=pos[y].size(); ++i) {
minn=min(minn, pre[i]-1);
// cout<<"minn: "<<minn<<endl;
// cout<<"pre: "<<pre[i]<<endl;
maxn=max(maxn, mp[y]+pre[i]-minn);
}
maxn=max(maxn, mp[y]+cnt[pos[y][0]]);
maxn=max(maxn, mp[y]+qcnt(pos[y][pos[y].size()-1], n));
for (int i=1; i<=pos[y].size(); ++i) {
maxn=max(maxn, i+qcnt(pos[y][i-1], n));
maxn=max(maxn, mp[y]-i+1+cnt[pos[y][i-1]]);
}
// cout<<"maxn: "<<maxn<<endl;
if (maxn>ans) ans=maxn, res.clear(), res.pb(uni[y]);
else if (maxn==ans) res.pb(uni[y]);
}
void solve() {
usiz=ans=0; sqr=sqrt(n); res.clear();
for (int i=1; i<=n; ++i) uni[++usiz]=a[i];
for (int i=1; i<=n; ++i) mp[i]=0, pos[i].clear();
sort(uni+1, uni+usiz+1);
usiz=unique(uni+1, uni+usiz+1)-uni-1;
for (int i=1; i<=n; ++i) ++mp[a[i]=lower_bound(uni+1, uni+usiz+1, a[i])-uni];
for (int i=1; i<=n; ++i) pos[a[i]].pb(i);
for (int i=1; i<=usiz; ++i) ans=max(ans, mp[i]);
for (int i=1; i<=usiz; ++i) if (mp[i]==ans) res.pb(uni[i]);
for (int i=1; i<=usiz; ++i) if (mp[i]>sqr) {
for (int j=1; j<=n+1; ++j) cnt[j]=cnt[j-1]+(a[j]==i);
for (int j=1; j<=usiz; ++j) if (i!=j) solve1(i, j), solve2(i, j);
}
for (int i=1; i<=usiz; ++i) buc[i]=0;
for (int i=1; i<=n; ++i) val[i]=0, que[i].clear();
for (int i=1; i<=n; ++i) lst[i]=buc[a[i]], buc[a[i]]=i;
for (int i=1; i<=usiz; ++i) if (mp[i]<=sqr) {
vector<int> tem;
tem.pb(0);
for (auto it:pos[i]) tem.pb(it);
tem.pb(n+1);
for (int j=0; j<tem.size(); ++j)
for (int k=j+1; k<tem.size(); ++k) if (tem[j]+1<=tem[k]-1)
que[tem[k]-1].pb({tem[j]+1, tem[k]-1, i, j+tem.size()-k-1}); //, cout<<"q: "<<tem[j]+1<<' '<<tem[k]-1<<' '<<j+tem.size()-k-1<<endl;
}
for (int i=1; i<=n; ++i) {
if (mp[a[i]]<=sqr) {
for (int now=i,cnt=1; now; now=lst[now],++cnt)
if (val[now]<cnt) {
for (int j=now; j&&val[j]<cnt; --j) val[j]=cnt;
}
}
for (auto it:que[i]) {
int maxn=val[it.l]+it.dlt;
if (maxn>ans) ans=maxn, res.clear(), res.pb(uni[it.id]);
else if (maxn==ans) res.pb(uni[it.id]);
}
}
printf("%d\n", ans);
sort(res.begin(), res.end());
res.erase(unique(res.begin(), res.end()), res.end());
for (auto it:res) printf("%d\n", it);
}
}
signed main()
{
freopen("mode.in", "r", stdin);
freopen("mode.out", "w", stdout);
int T=read();
while (T--) {
n=read();
for (int i=1; i<=n; ++i) a[i]=read();
// if (n<=300) force::solve();
// else task::solve();
task::solve();
}
return 0;
}