「解题报告」ARC128E K Different Values
我还是很菜啊。
先考虑判定问题。考虑先找出一些显然的必要条件。
记 \(m = \sum a_i\)。那么我们首先对 \(m\) 进行分块,每 \(k\) 个一块,设块数为 \(p\),最后一个块的大小为 \(q\)。
首先显然需要满足每个块内的数互不相同,这意味着 \(a_i \le p\)。
其次对于 \(a_i = p\) 的 \(i\) 的数量不超过 \(q\)。
然后发现这两个条件实际上也是充分条件。证明考虑首先先把 \(a_i = p\) 的全放到块里,然后对于每个 \(i\) 按顺序放进每个块内。块内从后往前放,容易发现这样一定满足条件。
那么我们考虑字典序最小。有一个很直接的贪心:
- 首先看当前 \(a_i=p\) 的个数有多少。假如个数等于 \(q\),那么就填可以填的最小的满足 \(a_i = p\) 的 \(i\);若没有,则无解。
- 假如个数小于 \(q\),那么就填最小的满足 \(a_i > 0\) 的 \(i\);若没有,则无解。
看起来挺离谱但好像挺对。考虑如何证明正确性。
正确性我们需要考虑两部分:
- 如果当前局面有解,那么按照这个策略放下每一个数后,剩下的数一定有解,也就是满足 \(\max a_i \le p\) 且 \(a_i = p\) 的数量 \(\le q\);
- 如果当前局面有解,那么按照这个策略来选数,每次都能选到数。
上述两个部分实际上是为了证明以当前选择的数为分割线,那么前半部分有解(前提),后半部分有解(第一部分),且跨过中间部分的位置也有解(第二部分)。
第一部分是很容易证明的,按照上面的选取方式,每次 \(a_i = p\) 会减少 \(1\),显然前者成立。
考虑第二部分。我们假设在位置 \(i\) 的地方找不到合法的可放的数,但这种情况有解,那么:
- 假如 \(a_i=p\) 的个数等于 \(q\),那么也就是前 \(k-1\) 个数中出现了所有的 \(a_i=p\) 的 \(i\),说明 \(q \le k-1\)。那么此时,如果删去最后 \(k-1\) 个数,所有满足 \(a_i = p\) 的 \(a_i\) 均会增加 \(1\),\(p\) 会增加 \(1\),这意味着此时 \(a_i=p\) 的数量不变,但是 \(q\) 一定会增加 \(1\),这与前面的第一部分 \(a_i=p\) 的数量 \(\le q\) 矛盾;
- 假如 \(a_i=p\) 的个数小于 \(q\),那么说明此时 \(a_i > 0\) 的个数小于等于 \(k-1\)。根据第一部分,当前局面有解,那么假如后面的空位大于等于 \(k\),说明至少还要有 \(k\) 个 \(a_i > 0\) 的位置,矛盾;假如空位小于 \(k\),说明 \(p=1\),那么此时所有的 \(a_i\) 应当都等于 \(p\),这与 \(a_i=p\) 的个数小于 \(q\) 矛盾。
因此直接按照上面的策略贪心即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 505, MAXM = 200005;
int n, k, m, a[MAXN];
int ans[MAXM];
bool vis[MAXN];
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
m += a[i];
}
bool flag = true;
for (int i = 1; i <= m; i++) {
int q = (m - i) % k + 1, p = (m - i) / k + 1;
int cnt = 0, fst = 0;
for (int j = 1; j <= n; j++) if (a[j] == p) {
if (!fst && !vis[j]) fst = j;
cnt++;
}
if (cnt != q) {
fst = 0;
for (int j = 1; j <= n; j++) if (a[j] && !vis[j]) {
fst = j;
break;
}
}
if (fst) {
ans[i] = fst;
a[fst]--;
} else {
flag = false;
break;
}
vis[ans[i]] = 1;
if (i >= k) vis[ans[i - k + 1]] = 0;
}
if (flag) {
for (int i = 1; i <= m; i++) {
printf("%d ", ans[i]);
}
} else {
printf("-1\n");
}
return 0;
}