牛客小白月赛29-B二进制
题目描述:
scimoon 有一个坏掉的计算器,这个计算器仅接受 (0sim 2^{20}-1) 的数
这个计算器只支持一种操作,举个例子,输入一个数 x,这个数会按顺序进行 n 次操作,在第 i 次操作中,有一个操作符 (op_i) 和一个数 (a_i)
如果 (op_i=1) 表示这次操作是将数 x 与 (a_i) 做 与运算
如果 (op_i=2) 表示这次操作是将数 x 与 (a_i) 做 或运算
如果 (op_i=3) 表示这次操作是将数 x 与 (a_i) 做 异或运算
操作过后 x 将会变为运算的结果
scimoon 觉得这个计算器非常地慢,于是他想对这 n 个运算做一些简化,这个艰巨的任务交给了你
具体而言,你的任务是:用不超过 5 次上面的操作,使得对于任何 (0le xle 2^{20}-1),你的操作的输出与计算器的输出一致
可以证明必然存在解
可能存在多组解,你只需要输出一组可能的解即可
输入描述:
第一行一个整数 n,表示计算器的操作次数
接下来 n 行,每行两个整数 op 与 a ,按顺序描述了每次操作
输出描述:
第一行一个 m,表示你的操作次数
你必须保证你输出的 (mle 5)
接下来 m 行每行仿照输入中 (op a) 的格式输出每次操作
示例:
输入
1
1 14514
输出
1
1 14514
备注
(n leq 5×10^5,1 leq op leq 3,0 leq a leq 2^{20}−1)
题解:
位运算是可以有捷径的,分析可以发现,对于一个数位存在如下情况:
- 与(&)运算对应位为1,原数位不变
- 与(&)运算对应位为0,原数位为0
- 或(|)运算对应位为1,原数位为1
- 或(|)运算对应位为0,原数位不变
- 异或(^)运算对应位为1,原数位取反
- 异或(^)运算对应位为0,原数位不变
所以我们只考虑可能改变数位的操作,即如下三种:
- x&0 = 0
- x|1 = 1
- x ^1=!x
因此我们只需要考虑每次操作的数的各个位数,和执行的运算:
- 与运算,数位为0,之前所有操作无效,只需对该位执行&0操作
- 或运算,数位为1,之前所有操作无效,只需对该位执行|1操作
- 异或运算,数位为1,若上一异或位不为1,则执行一次^1操作,否则不执行
代码:
#include <iostream>
#include <cmath>
using namespace std;
int v[21][4];
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
for(int i = 0; i < n; ++i) {
int op, w;
cin >> op >> w;
for(int j = 0; j <= 20; ++j) {
if(op == 1 && (w & 1) == 0) {
v[j][0] = 1;
v[j][1] = 0;
v[j][2] = 0;
}
else if(op == 2 && w & 1 == 1) {
v[j][1] = 1;
v[j][0] = 0;
v[j][2] = 0;
}
else if(op == 3 && w & 1 == 1) {
if(v[j][2]) v[j][2] = 0;
else v[j][2] = 1;
}
w >>= 1;
}
}
int a = 0, b = 0, c = 0;
for(int i = 0; i < 20; ++i) {
if(v[i][0]) continue;
a += (1 << i);
}
for(int i = 0; i < 20; ++i) {
if(v[i][1]) {
b += (1 << i);
}
}
for(int i = 0; i < 20; ++i) {
if(v[i][2]) {
c += (1 << i);
}
}
printf("3
1 %d
2 %d
3 %d
", a, b ,c);
return 0;
}