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;
}

浙公网安备 33010602011771号