题解:CF2134E Power Boxes
闲话:E <<<< D,这怎么能放到 2E 的?
设 \(f_i\) 指扔到坐标 \(i\) 要跳多少次,可以通过「投掷」操作得到;设\(num_i\) 为第 \(i\) 个位置的能量值。对于已经确定 \(num_i\) 的区间 \([l,n]\),可以递推求出 \(f_i\),即 \(f_i=f_{i+num_i}+1\)。
考虑如何确定 \(num_i\),根据投掷次数 \(f_i\) 的关系,在 \(f_{i+1}\not=f_{i+2}\) 时,只需进行一次「投掷」操作即可得到 \(num_i\),为 \(\begin{cases}1,&f_i=f_{i+1}+1\\2,&f_i=f_{i+2}+1\end{cases}\)。这种情况下,求出一个盒子的能量值操作次数是 \(1\)。下面考虑 \(f_{i+1}=f_{i+2}\) 的情况。
注意到一个事实:第 \(n-1\) 个位置只需「投掷」一次就能确定,也就是 \(num_i=\begin{cases}1,&f_{i}=2\\2,&f_i=1\end{cases}\)。同理的,我们进行「交换」操作,对 \((n-1,n)\) 进行交换后,原先的 \(num_n\) 变为 \(num_{n-1}\),我们如法炮制,对 \(n-1\) 再进行「投掷」操作,我们就确定了 \(n\) 与 \(n-1\) 的答案。平均下来求出一个盒子的操作次数是 \(\dfrac 32\)。
这个做法是否能推广呢?实际上,对于 \(i>n\) 的所有 \(f_i\),我们可以将其视作为 \(0\)。对于第 \(n\) 个盒子,实际上就是一种特殊的 \(f_{i+1}=f_{i+2}\) 情况。我们既然无法确定这种情况下的 \(num_i\),是否能确定 \(num_{i-1}\)?此时 \(f_i=f_{i+1/2}+1\),那么 \(f_{i-1}\) 的值一定能推断出 \(num_{i-1}\) 的值。具体而言,当 \(f_{i-1}=f_{i+1}+1\),那么 \(num_{i-1}=2\)。反之,\(num_{i-1}=1\)。对于 \(i\),我们只需进行「交换」操作,同理地计算即可。平均一个盒子的操作次数是 \(\dfrac32\)。
考虑一个 corner case:如果对于 \(i=1\),出现 \(f_{i+1}=f_{i+2}\),如何操作?可以断言:若 \(f_{i+1}=f_{i+2}\),则 \(f_{i+2}\not=f_{i+3}\),这是由于每个格子只能跳 \(1\) 或 \(2\) 格。因此只需交换 \(1\) 与 \(2\),按照第一种情况计算即可。
每一次「交换」后需要重新计算 \(f\),因此时间复杂度是 \(\mathcal O(\sum n^2)\),求和是多测带来的。
void upd () {
for (int i = 1; i <= n + 2; i ++) f[i] = 0;
for (int i = n; i; i --)
if (!num[i]) break ;
else f[i] = f[i + num[i]] + 1;
}
int Throw (int x) {
opera ++;
printf ("throw %d\n", x);
fflush (stdout); int ret; scanf ("%d", &ret);
return ret;
}
void Swap (int x) {
opera ++;
swap (id[x], id[x + 1]);
swap (num[x], num[x + 1]);
upd ();
printf ("swap %d\n", x);
fflush (stdout);
}
void getval (int x) {
int th1 = Throw (x);
if (th1 == f[x + 2] + 1) num[i - 1] = 2;
else num[i - 1] = 1;
}
void work () { opera = 0;
scanf ("%d", &n); for (int i = 1; i <= n + 2; i ++) id[i] = i, num[i] = 0, f[i] = 0;
for (int i = n; i; i --) {
if (num[i]) continue;
upd ();
if (f[i + 1] == f[i + 2]) {
if (i == 1) {
Swap (1);
getval (2);
break ;
}
getval (i - 1);
Swap (i - 1);
getval (i - 1);
} else {
upd ();
int th = Throw (i);
f[i] = th;
if (th == f[i + 1] + 1) num[i] = 1;
else num[i] = 2;
}
}
for (int i = 1; i <= n; i ++) fprintf (stderr, "%d ", num[i]);
printf ("! ");
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) if (id[j] == i) printf ("%d ", num[j]);
}
fflush (stdout);
for (int i = 1; i <= n; i ++) fprintf (stderr, "f[%d] = %d ", id[i], f[id[i]]);
fprintf (stderr, "\nCommands were runned %d times, with the limit = %d.\n", opera, (n * 3 + 1) / 2);
puts ("");
}

浙公网安备 33010602011771号