Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)D. Take a Guess

传送门

我讨厌交互题,也不喜欢位运算

这一题真是符合我的胃口

首先如果知道 $a,b$ 两个未知数的 按位并值 和 按位或值

则可以知道 $a,b$ 两个数的 按位异或值:

$a&b=x,a|b=y$ 则 $a$ ^ $b=x$ ^ $y$ ,这个算是经典结论了,证明只要分类讨论一下即可

所以先花 $2n-2$ 次询问把数量所有相邻的两个数的 $&$ 和 $|$ 得到

然后就可得到相邻数的异或和,再根据异或的简单性质,我们就能知道任意两个数的异或和是多少

现在如果我们能得到某个位置的具体值,就可以利用任意两数的异或和来反向推导每个数了

不妨求第一个数的值,最后两次询问可以选择问第一个数和第三个数的 并 与 或,这样就有 $a_1$ , $a_2$ , $a_3$ 两两的 并 与 或

然后进行一波分类讨论发现可以确定第一个数二进制的每一位(具体怎么分类讨论自己动动笔就好了)

然后就做完了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e4+7;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
int n,k,ans[N];
int a[N],o[N],x[N],xr[N];
int main()
{
    n=read(),k=read();
    for(int i=1;i<n;i++)
    {
        printf("and %d %d\n",i,i+1);
        fflush(stdout);
        a[i]=read();
    }
    for(int i=1;i<n;i++)
    {
        printf("or %d %d\n",i,i+1);
        fflush(stdout);
        o[i]=read();
    }
    for(int i=1;i<=n;i++)
        x[i]=a[i]^o[i];
    for(int j=2;j<=n;j++)
        xr[j]=xr[j-1]^x[j-1];
    printf("and 1 3\n");
    fflush(stdout);
    int b=read();
    printf("or 1 3\n");
    fflush(stdout);
    int c=read();
    int a1=0;
    for(int i=0;i<=30;i++)
    {
        int x=a[1]&(1<<i),y=b&(1<<i),z=a[2]&(1<<i);
        if(x||y) { a1|=(1<<i); continue; }
        if(z) continue;
        if(o[1]&(1<<i) && c&(1<<i))
            a1|=(1<<i);
    }
    ans[1]=a1;
    for(int i=2;i<=n;i++)
        ans[i]=a1^xr[i];
    sort(ans+1,ans+n+1);
    cout<<"finish "<<ans[k]<<endl;
    return 0;
}

 

posted @ 2021-09-03 21:15  LLTYYC  阅读(57)  评论(0编辑  收藏  举报