题解:AT_wtf19_a Magic
posted on 2024-12-28 07:51:53 | under | source
来自官方题解做法。
千万别往博弈上去想,因为你压根不知道对方咋操作。题意实际上是:确定一种方案 \(A\),满足无论魔术师怎么移动你都能找到宝藏。
我们记 \(B\) 为宝箱的移动过程,则 \(|B|\le k+1\),同时相邻元素不能相同,因为无意义。考虑对于 \(B\) 判断 \(A\) 能否找到宝藏,首先从 \(A_1\) 开始找,直到 \(A_{p_1}=B_1\),那么魔术师必须在打开 \(A_{p_1}\) 前将宝箱移至 \(B_2\),以此类推。可以发现等价于判定 \(B\) 是否是 \(A\) 的子序列,如果是的话就可以找到宝藏,反之不行。
于是合法的 \(A\) 满足任意 \(B\) 都是其子序列,显然我们只需关心大小为 \(k+1\) 的 \(B\) 即可。思考构造过程,记 \(c_1\dots c_n\),其中 \(c_i\) 含义是:所有以 \(i\) 为开头且大小为 \(c_i\) 的子序列都在构造的序列中出现。
有个浅显的性质:假如 \(c_i<c_j\),那么其实无需考虑 \(c_i\) 的限制,即赋值 \(0\),因为 \(c_j\) 必然包含了 \(c_i\) 的所有可能序列。
我们从左到右依次确定 \(A\),并更新 \(c\)。假设 \(A_1=x\),那么对于 \(c_x\),可以将限制拆为对于任意 \(y\ne x\),满足所有开头为 \(y\) 且大小为 \(c_x-1\) 的子序列都出现。即 \(\forall y,c_y\gets \max(c_y,c_x-1)\),然后 \(c_x\gets 0\)。并根据性质将非最大值元素赋值为 \(0\)。
模拟该过程:一开始 \(c=\{k+1,\dots,k+1\}\),操作一次变成 \(c=\{k+1,\dots k+1,0\}\)。接着再选择一个 \(k+1\) 赋值为零(显然对 \(0\) 操作无意义),直到全都是 \(0\),然后 \(c=\{k,\dots k,0\}\),以此类推。
可以发现,构造出来的 \(A\) 满足 $[1,k],[k,2k-1],[2k-1,3k-2]\dots $ 均为排列。即 \(k+1\) 个排列首尾相连并去除重复元素。
但是此题还有对元素出现次数的限制,怎么办呢?对于元素 \(x\),假如其出现次数 \(a_i<k+1\) 就需要将其放在两排列的重合处,这样可以让它同时在两个排列上出现。即至少要在 \(q_i=k+1-a_i\) 个重合处出现。那么 \(\sum q>k\) 就一定无解。
但是还有一个限制,相邻重合处元素不能相同。
考虑这样一个问题:给出 \(m\) 种元素及其出现次数 \(q_i\),需要将它们排列使得相邻元素不相同。很难不往绝对众数上思考。我们断言:假如 \(\max q\le \lceil \frac {\sum q}2\rceil\) 就有解,反之无解。
必要性显然。充分性考虑归纳,每次取走出现次数最大和次大的填上去,不难发现仍然满足条件。故成立。
回到本题,还差最后一步,需要将一些 \(q\) 增加使得总和 \(=k\),那么显然优先增加较小的。
复杂度 \(O(nk)\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e2 + 5;
int n, k, a, cnt, b[N], c[N], cn, mx;
signed main(){
cin >> n >> k;
for(int i = 1; i <= n; ++i){
scanf("%d", &a);
if(a < k + 1) b[i] = k + 1 - a, cnt += b[i], mx = max(mx, b[i]);
}
while(cnt < k){
int mi = 0;
for(int j = 1; j <= n; ++j)
if(!mi || b[j] < b[mi]) mi = j;
mx = max(mx, ++b[mi]), ++cnt;
}
if(cnt > k || mx > (k + 1) / 2) puts("-1");
else{
printf("%d\n", n * (k + 1) - k);
while(1){
int ma = 0, ma2 = 0;
for(int j = 1; j <= n; ++j){
if(!b[j]) continue;
if(b[j] >= b[ma]) ma2 = ma, ma = j;
else if(b[j] > b[ma2]) ma2 = j;
}
if(!ma && !ma2) break;
c[++cn] = ma, --b[ma];
if(ma2) c[++cn] = ma2, --b[ma2];
}
for(int i = 1; i <= k; ++i){
for(int j = 1; j <= n; ++j)
if(j != c[i - 1] && j != c[i]) printf("%d ", j);
printf("%d ", c[i]);
}
for(int i = 1; i <= n; ++i) if(i != c[k]) printf("%d ", i);
}
return 0;
}

浙公网安备 33010602011771号