Codeforces
题解
]
CF1451E2 Bitwise Queries (Hard Version) 题解
看着这到题的题解我感觉都写的很诡异,所以我决定贡献一发题解。
题目大意:有一个长度为 $n$ 的数组( $n$ 是 $2$ 的幂),有 $3$ 种操作,AND OR XOR,可以获得数组两个元素的 AND OR XOR 值,仅限 $n+1$ 次操作求原数组。
首先考虑以 $a_1$ 为基准求出 $a_1\oplus a_k$,这样只要求出 $a_1$ 就可以求出整个数组,这一步需要 $n-1$ 次操作。
因为只有 $n$ 个元素,元素的范围是 $[0,n-1]$,所以可以进行分类讨论:
1.所有元素并不是互不相同。也就是说有重复的元素,这样只要找到相同的元素,对相同的元素进行 AND 操作就可以求出这个元素(因为知道了这两个元素和 $a_1$ 的异或值),又因为知道了所有元素和 $a_1$ 的 XOR,所以可以直接算出整个数组。考虑如何找到相同的元素,可以开个桶记录每个元素出现的次数。取最大的出现次数(这种情况下至少两次),然后进行一次查找就可以得到所有异或 $a_1$ 相同的元素也就是相同的元素,任意取两个询问 AND 就可以了。不过这里有个要注意的,如果 $a_1$ 恰好是重复的元素只会扫到 $1$ 个不一样的元素,取 $1$ 询问即可。
2.所有元素互不相同。也就是是 $[0,n-1]$ 的全排列。考虑 XOR 的性质,相同就是 $0$,也就是说可以选择 $1$ 这个数,只有最后一位是 $1$,其他都是 $0$,也就是说如果 $a_1\oplus a_k=1$,$a_1$ 和 $a_k$ 只有最后一位不一样,可以通过 AND 操作求出前 $\log n-1$ 位。同理如果 $a_1\oplus a_k=\frac n2$,$a_1$ 和 $a_k$ 只有第一位不一样,可以通过 AND 操作求出后 $\log n-1$ 位。把两个结果组合一下(直接或起来)就可以得到 $a_1$。其实不一定要选择 $1$ 和 $\frac n2$,只要保证这两个数二进制下每位都至少有个数是 $0$ 就行。
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
constexpr int kN=(1<<16)+7;
int xr[kN],n,cnt[kN];
using std::cin,std::cout,std::endl;
auto AND=[](int a,int b){cout<<"AND "<<(a)<<' '<<(b)<<endl;int t;cin>>t;return t;};
auto XOR=[](int a,int b){cout<<"XOR "<<(a)<<' '<<(b)<<endl;int t;cin>>t;return t;};
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
std::ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;cnt[0]=1;
for(int i=2;i<=n;i++)cnt[xr[i]=XOR(1,i)]++;
if(int p=std::max_element(cnt,cnt+n)-cnt;cnt[p]!=1){// 有重复
std::basic_string<int>g;
for(int i=2;i<=n;i++)if(xr[i]==p)g+=i;
int ak=AND(g.size()==1?1:g.front(),g.back()),a1=ak^p;
cout<<"! "<<a1;
for(int i=2;i<=n;i++)cout<<' '<<(a1^xr[i]);
}else{// 没重复
int xp=-1,yp=-1,tx=1,ty=n>>1;
for(int i=2;i<=n;i++){
if(xr[i]==tx)xp=i;
if(xr[i]==ty)yp=i;
}
int a1=AND(1,xp)|AND(1,yp);
cout<<"! "<<a1;
for(int i=2;i<=n;i++)cout<<' '<<(a1^xr[i]);
}
cout.flush();
return 0;
}