题解: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;
}
posted @ 2026-01-15 08:16  Zwi  阅读(2)  评论(0)    收藏  举报