CF786C Till I Collapse 题解

题目传送门

思路

根号分治。

首先我们会发现答案一定不会超过 \(\lceil \dfrac{n}{k} \rceil\),因为每 \(k\) 个分为一组也就这么多。同时 \(k\) 变大时,答案一定不会变大,即答案序列单调不增。这样就可以得出,不同的答案最多只有 \(\sqrt n\) 个,且答案序列中相同的元素一定在同一连续子序列中。

那如果 \(k \le \sqrt n\),直接暴力统计。这部分的时间复杂度为 \(\mathcal{O}(n \sqrt n)\)。对于 \(k > \sqrt n\),此时的答案一定 \(\le \dfrac{n}{\sqrt n}\),最多只有 \(\sqrt n\) 种不同的答案。我们在求得这些不同的答案后,二分找到最右边的 \(i\) 使得 \(k = i\) 时的答案与现在的答案相等。然后直接将这一段输出。因为最多只会有 \(\sqrt n\) 种不同的答案,所以最多会有 \(\mathcal{O}(n \sqrt n \log n)\)。综上,总时间复杂度为 \(\mathcal{O}(n \sqrt n \log n)\),可以通过此题。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;

int n;
int a[N];
bool st[N];

int calc(int k)
{
	memset(st, 0, sizeof(st));
	int lst = 0, cnt = 0, res = 1;
	for (int i = 1; i <= n; i++)
	{
		if (!st[a[i]]) st[a[i]] = 1, cnt++;
		if (cnt > k)
		{
			cnt = 1, res++;
			for (int j = lst; j < i; j++) st[a[j]] = 0;
			st[a[i]] = 1;
			lst = i;
		}
	}
	return res;
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	for (int i = 1; i <= n; i++)
	{
		if (i <= (int)sqrt(n)) printf("%d ", calc(i));
		else
		{
			int val = calc(i);
			int l = i, r = n, res = 0;
			while (l <= r)
			{
				int mid = (l + r) >> 1;
				if (calc(mid) < val) r = mid - 1;
				else l = mid + 1, res = mid;
			}
			for (int j = i; j <= res; j++)
				printf("%d ", val);
			i = res;
		}
	}
	return 0;
}
posted @ 2026-04-01 19:37  lucasincyber  阅读(16)  评论(0)    收藏  举报