E Bitwise Queries
题目链接
easy和hard
交互题
题意:
给一个长度为
n
n
n的数组,元素为从
[
0
,
n
−
1
]
[0,n - 1]
[0,n−1]。刚开始给你长度
n
n
n,n是2的次幂,让猜这个数组,每次询问有可以是三种中的一种:
XOR i j //返回a[i] ^ a[j]
AND i j // 返回a[i] & a[j]
OR i j // 返回a[i] | a[j]
easy让询问不超过 n + 2 n + 2 n+2次、hard让询问不超过 n + 1 n + 1 n+1次。让猜出这个数组。
这怎么做。。
好难,不会。。我是只会搜题解的laji
求出一个值后就可以通过异或把其他的值全求出来。
因为 a[i] ^ a[j] = x -> a[j] = x ^ a[i]
然后有这样一个公式:
x + y = (x ^ y) + 2 * (x & y)
只要知道
a
[
1
]
+
a
[
2
]
、
a
[
2
]
+
a
[
3
]
、
a
[
1
]
+
a
[
3
]
a[1] + a[2]、a[2] + a[3]、a[1] + a[3]
a[1]+a[2]、a[2]+a[3]、a[1]+a[3]的值就可以解方程得出来这三个的值了。
于是询问a[1] ^ a[2]、 a[1] & a[2] 可以得出
a
[
1
]
+
a
[
2
]
a[1] + a[2]
a[1]+a[2] 于是再查询a[1] ^ a[3]、a[1] & a[3]、a[2] & a[3] 就好了,不用查询 a[2] ^ a[3]
因为 a[2] ^ a[3] == a[1] ^ a[2] ^ a[1] ^ a[3] 可以直接计算出来,省了一次查询,然后剩下的n - 3个数查询与a[1]的异或得到。于是查询次数为:
5
+
n
−
3
=
n
+
2
5 + n - 3 = n + 2
5+n−3=n+2。满足easy。
hard怎么办?
有两个条件:n是2的次幂、a数组中的值为
[
0
,
n
−
1
]
[0,n-1]
[0,n−1].
这两个条件怎么用?可以看出如果没有重复的元素,对于数组a中的每一个值a[i] 都存在一个a[j]满足:a[i] ^a[j] == n - 1,并且a[i] & a[j] == 0.
所以没有重复元素的时候可以知道存在一个值与a[1] 异或起来是n - 1,那么照easy的解法来看就可以省去一次异或查询,先查所有的值与a[1]异或的结果存在b数组中,花费n - 1次,然后在b数组中找n - 1值的位置k,再随便找一个位置x。这样的话 a[k] ^ a[x] = b[k] ^ b[x] 所以只用查询 a[1] & a[x]、a[k] & a[x] 就好了,花费
n
−
1
+
2
=
n
+
1
n - 1 + 2 = n + 1
n−1+2=n+1次。
所以有重复元素的怎么办呢?
有重复元素可以发现如果
a
[
i
]
=
=
a
[
j
]
a[i] == a[j]
a[i]==a[j]有a[i] ^ a[1] = a[j] ^ a[1] 并且a[i] = a[i] & a[j] 所以只用查询所有的数与a[1]的异或值,肯定有两个相等的数,记下这两个的位置,然后查询a[k1] & a[k2] 就好了。总共查询n 次。
好了,这就是题解了。。。 好难呀。 想不到。
代码:
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
/** Init-Start*/
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
#define pb push_back
#define mkp make_pair
#define st first
#define sd second
const int maxn =1e6+5;
const int M = 4005;
const ll mod = 998244353;
int a[maxn];
int ans[maxn];
int vis[maxn];
int main()
{
int n;
scanf("%d",&n);
vis[0] = 1;
int k1 = -1, k2 = -1, s = 0;
for (int i= 2; i <= n; i ++ )
{
printf("XOR %d %d\n", 1, i);
fflush(stdout);
scanf("%d",&a[i]);
if(vis[a[i]])
{
s = a[i];
k1 = i;
k2 = vis[a[i]];
}
vis[a[i]] = i;
}
if(k1 != -1) // 有 相同的。
{
int t;
printf("AND %d %d\n", k1, k2);
fflush(stdout);
scanf("%d",&t);
ans[k1] = t;
ans[k2] = t;
ans[1] = a[k1] ^ t;
for (int i = 2; i <= n; i ++ )
{
ans[i] = ans[1] ^ a[i];
}
}
else
{
// 有一个与a1 对应
int s = 1;
int k = 1;
for (int i = 2; i <= n; i ++ )
{
if(a[i] != n - 1)
{
k = i;
}
else
s = i;
}
int t;
printf("AND %d %d\n", 1, k);
fflush(stdout);
scanf("%d",&t);
int ts;
printf("AND %d %d\n",s,k);
fflush(stdout);
scanf("%d",&ts);
int k1 = a[k] + 2 * t;
int s1 = n - 1;
int sk = (a[k] ^ a[s]) + 2 * ts;
ans[1] = (k1 + s1 - sk) / 2;
for(int i= 2; i <= n; i ++ )
{
ans[i] = ans[1] ^ a[i];
}
}
printf("!");
for (int i = 1; i <= n; i ++ )
{
printf(" %d", ans[i]);
}
printf("\n");
}