Codeforces Round #685 (Div. 2) E Bitwise Queries 位运算 妙啊

E Bitwise Queries

题目链接
easy和hard
交互题
题意:
给一个长度为 n n n的数组,元素为从 [ 0 , n − 1 ] [0,n - 1] [0,n1]。刚开始给你长度 n n nn是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+n3=n+2。满足easy。
hard怎么办?
有两个条件:n是2的次幂、a数组中的值为 [ 0 , n − 1 ] [0,n-1] [0,n1].
这两个条件怎么用?可以看出如果没有重复的元素,对于数组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 n1+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");
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值