【CF1451E2】Bitwise Queries (Hard Version)
题目
题目链接:https://codeforces.com/problemset/problem/1451/E2
Ridbit 有一个隐藏的长度为 \(n\) 的整数数组 \(a\),想让 Ashish 去猜,注意 \(n\) 是 \(2\) 的整数次幂。Ridbit 允许 Ashish 提出三种不同类型的查询。它们分别是:
AND \(i\) \(j\): 求元素 \(a_i\) 和 \(a_j\) 每一位的 \(and\) (\(1≤i\),\(j≤n\),\(i≠j\))
OR \(i\) \(j\): 求元素 \(a_i\) 和 \(a_j\) 每一位的 \(or\) (\(1≤i\),\(j≤n\),\(i≠j\))
XOR \(i\) \(j\): 求元素 \(a_i\) 和 \(a_j\) 每一位的 \(xor\) (\(1≤i\),\(j≤n\),\(i≠j\))
有限制:(1) Ashish 最多可以询问 \(n + 1\) 次。(2) \(a\) 数组满足 \(0 \le a_i \le n - 1\)。
\(n\)在\(4 \leq n \leq 2^{16}\),也就是数组的长度。同时保证\(n\)是\(2\)的整数次幂。
思路
首先肯定把 \(2\sim n\) 元素与 \(1\) 号元素异或一遍,然后考虑在 \(2\) 次询问内得到 \(1\) 号元素。
分两类情况讨论:
- 存在两个元素相等,那么我们直接把这两个元素找出来(要么两个异或 \(1\) 号元素相等,要么存在一个元素疑异或 \(1\) 号元素为 \(0\))。把他们
OR
一下就可以得到了。 - 不存在两个元素相等,那么必然是 \(0\sim n-1\) 每一个数字恰好出现一次。那么找到异或 \(1\) 号元素为 \(1\) 的元素,询问它和 \(1\) 号元素的
OR
,就可以得到 \(1\) 号元素的前 \(\log n-1\) 位,再找到异或一号元素等于 \(\frac{n}{2}\) 的元素,询问它与 \(1\) 号元素的OR
,就可以得到 \(1\) 号元素的后 \(\log n-1\) 位。综合一下就可以得到 \(1\) 号元素了。
这样显然操作次数最多为 \(n+1\),时间复杂度 \(O(n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=(1<<16)+10;
int n,flag,a[N],b[N],pos[N];
int main()
{
scanf("%d",&n);
pos[0]=1;
for (int i=2;i<=n;i++)
{
printf("XOR 1 %d\n",i); fflush(stdout);
scanf("%d",&a[i]);
if (pos[a[i]]) flag=pos[a[i]];
pos[a[i]]=i;
}
if (flag)
{
printf("OR %d %d\n",flag,pos[a[flag]]); fflush(stdout);
scanf("%d",&b[flag]);
b[pos[a[flag]]]=b[flag];
b[1]=a[flag]^b[flag];
}
else
{
int x=0,y=0;
for (int i=2;i<=n;i++)
{
if (a[i]==1)
{
printf("OR 1 %d\n",i); fflush(stdout);
scanf("%d",&x);
}
if (a[i]==(n>>1))
{
printf("OR 1 %d\n",i); fflush(stdout);
scanf("%d",&y);
}
}
b[1]=((x>>1)<<1)|(y&1);
}
printf("! %d",b[1]);
for (int i=2;i<=n;i++)
printf(" %d",b[1]^a[i]);
fflush(stdout);
return 0;
}