题解:CF2134E Power Boxes

首先考虑出在什么情况下在位置 xxx 上进行 throw x\texttt{throw x}throw x 操作可以确定该位置上的值。设 fxf_xfx 表示从 xxx 开始扔球时会进行的次数。若 fx+1=fx+2f_{x + 1} = f_{x + 2}fx+1=fx+2,显然 fx=fx+1+1f_x = f_{x + 1} + 1fx=fx+1+1,此时无法确定 xxx 位置上的值。若 fx+1≠fx+2f_{x + 1} \neq f_{x + 2}fx+1=fx+2,此时可以确定。具体来说,有:

ansx={1if fx=fx+1+12if fx=fx+2+1 ans_x = \begin{cases} 1 & \texttt{if}\ f_{x} = f_{x + 1} + 1\\ 2 & \texttt{if}\ f_{x} = f_{x + 2} + 1 \end{cases} ansx={12if fx=fx+1+1if fx=fx+2+1

剩下考虑形如 fx+1=fx+2f_{x + 1} = f_{x + 2}fx+1=fx+2,而 xxx 位置上值未知的情况。由于 fx=fx+1+1>fx+1f_x = f_{x + 1} + 1 > f_{x + 1}fx=fx+1+1>fx+1,因此 x−1x - 1x1 位置上的值必定已知。尝试进行 swap x\texttt{swap x}swap x 操作,形成新的关系必定满足 fx=fx+1+1=fx+2+1f_x = f_{x + 1} + 1 = f_{x + 2} + 1fx=fx+1+1=fx+2+1,此时若 fx−1+1=fx+1f_{x - 1} + 1 = f_{x + 1}fx1+1=fx+1,则说明原来 xxx 位置上的值为 222,否则为 111

特别的,考虑 111 位置上未知的情况。尝试 swap 1\texttt{swap 1}swap 1 后进行 throw 2\texttt{throw 2}throw 2 操作,若 f2=f3+1f_2 = f_3 + 1f2=f3+1,则说明原来 111 位置上的值为 111,否则为 222

最后来证明操作数不超过 ⌈3n2⌉\lceil\frac{3n}{2}\rceil23n

证明
如上所述,throw x\texttt{throw x}throw x 在每个位置上操作有且仅有 111 次。若 fx+1≠fx+2f_{x + 1} \neq f_{x + 2}fx+1=fx+2,则不需要进行 swap x\texttt{swap x}swap x 操作,否则必然有 fx−1≠fxf_{x - 1} \neq f_xfx1=fx。因此 swap x≤⌈n2⌉\texttt{swap x} \le \lceil \frac{n}{2} \rceilswap x2n。命题得证。

写的时候需要注意,在 swap x\texttt{swap x}swap x 以后需要更新 fx−1,fxf_{x - 1},f_{x}fx1,fx 的值,防止出现错误!!!

代码如下:

#include <bits/stdc++.h>
#define pii pair <int,int>
#define init(x) memset (x,0,sizeof (x))
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
using namespace std;
const int MAX = 1e5 + 5;
const int MOD = 1e9 + 7;
inline int read ();
int query (string op,int x)
{
    cout<<op<<" "<<x<<endl;fflush (stdout);
    if (op == "throw") return read ();
    else return -1;
}
void solve ()
{
    int n = read ();
    vector <int> ans (n + 1),f (n + 3,0),d (n + 1);
    for (int i = n;i;--i)
    {
        if (f[i + 1] != f[i + 2])
        {
            f[i] = query ("throw",i);
            ans[i] = d[i] = 1 + (f[i] != f[i + 1] + 1);
        }
        else f[i] = f[i + 1] + 1;
    }
    for (int i = n;i;--i)
    {
        if (ans[i])
        {
            f[i] = f[i + d[i]] + 1;
            continue;
        }
        if (f[i + 1] != f[i + 2])
        {
            f[i] = query ("throw",i);
            ans[i] = d[i] = 1 + (f[i] != f[i + 1] + 1);
            continue;
        }
        f[i] = f[i + 1] + 1;
        if (i == 1)
        {
            int x = query ("swap",1);
            if (f[2] + 1 == query ("throw",2)) ans[1] = 1;
            else ans[1] = 2;
        }
        else
        {
            int x = query ("swap",i - 1);
            f[i] = query ("throw",i - 1);
            ans[i] = d[i] = 1 + (f[i] == f[i + 1] + 1);
            swap (f[i - 1],f[i]);swap (d[i - 1],d[i]);
            f[i] = f[i + ans[i]] + 1;
        }   
    }
    cout<<"! ";
    for (int i = 1;i <= n;++i) cout<<ans[i]<<" \n"[i == n];
    fflush (stdout);
}
int main ()
{
    int t = read ();
    while (t--) solve ();
    return 0;
}
inline int read ()
{
    int s = 0;int f = 1;
    char ch = getchar ();
    while ((ch < '0' || ch > '9') && ch != EOF)
    {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9')
    {
        s = s * 10 + ch - '0';
        ch = getchar ();
    }
    return s * f;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值