「解题报告」ARC130E Increasing Minimum
想法大概差不多,但是确实不知道咋维护(
考虑每次删最小值的过程,我们相当于先删掉所有最小值 \(=1\) 的位置,然后删所有最小值 \(=2\) 的位置,依次类推。
那么我们可以将删除的序列划分成若干段,每一段都是最小值相等时的位置。首先这一段中肯定不能有重复元素,其次后一段中一定包含了前一段中的所有元素。
而字典序最小这个限制其实是很没用的,因为按照这个顺序操作容易发现相对大小关系是不会发生改变的,那么我们肯定就应当是尽可能让每一位的数都更小。这其实就相当于尽可能让划分的段数最少。
考虑如何实现这个东西:我们可以确定出每一个位置可不可以作为某一段的结尾,且作为的段数最少是多少。首先统计出不同的数有多少,对于某一个位置,如果它的一个后缀恰好出现了当前所有不同的数,那么这一段就是可以划分的,否则就不能划分。那么根据这个转移即可。
而最后一段是很特殊的,如果我们能找到最后一段,删除最后一段之后,最后得到的序列应该所有值都相等。而最后一段中的元素也互不相同,所以我们可以在这一段后缀中寻找最小的划分方案,若段数相等则尽可能位置靠后(最后一段越短,最后被 \(+1\) 的数就越少)。如果找不到就是 \(-1\)。
然后直接根据划分输出答案即可。简单的实现是我们知道删掉最后一段后每个数的大小,所以可以反着再减掉。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
int n, k;
int a[MAXN], b[MAXN];
int lst[MAXN];
int f[MAXN];
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= k; i++) {
scanf("%d", &a[i]);
}
int pre = 0, cnt = 0;
for (int i = 1; i <= k; i++) {
pre = max(pre, lst[a[i]]);
if (!lst[a[i]]) cnt++;
lst[a[i]] = i;
if (i - pre == cnt) f[i] = f[i - cnt] + 1;
else f[i] = INT_MAX / 2;
}
int pos = -1;
for (int i = pre; i <= k; i++) if (pos == -1 || f[i] <= f[pos]) pos = i;
if (f[pos] >= INT_MAX / 2) {
printf("-1\n");
return 0;
}
for (int i = 1; i <= n; i++) b[i] = f[pos] + 1;
for (int i = 1; i <= pos; i++) b[a[i]]--;
for (int i = 1; i <= n; i++) {
printf("%d ", b[i]);
}
return 0;
}