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; }