题解 [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;
}
posted @ 2022-05-16 21:26  Administrator-09  阅读(0)  评论(0编辑  收藏  举报