题解: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 ("");
}
posted @ 2025-08-28 17:36  Toorean  阅读(13)  评论(0)    收藏  举报