题目链接:
https://www.acwing.com/problem/content/description/1000/
ACwing博客:https://www.acwing.com/user/myspace/index/105140/
### 题目描述
NOI传统风格,很多废话,大意其实就是:
给定一个数M,和N组位计算
求在0到M之间的任意数进行这N组运算之后的最大的答案。
### 算法详解
首先知道位运算的一个重要特征:
除了位移以外的每个位运算,**都与其他位无关,只与本位有关**
也就是说,进行相同的位运算,00100 和 11100 两数的后三位结果是一样的
而我们又知道:
**0的二进制是:00000000…………
-1的二进制是:1111111111…………**
所以!就有了一个绝妙的想法!
只用0和-1进行位运算,就可以的到任何一位的任何情况进行位运算的结果
这只需要计算2*n次计算,而且不需要开数组!!
接下来,只需要从大到小枚举每一个位,枚举条件也很简单:
1,如果这个位用1就大于M,那么只好用0;
2,如果这个位用0得到的结果位是1,那么果断用0
3,如果1,2都不满足,且用1得到结果为1,那么用1
4,如果用1还是用0结果都是0,还是用0(给后面留下操作空间,尽量满足条件1)
这么这题的O(n)算法就出炉了!
##### C++实现
```
#include <iostream>
#define LL long long
using namespace std;
char a[3];
LL n,m,op,tr0,tr1,ans;
int main(){
cin >> n >> m;
tr0=0;tr1=-1;
for(int i=0;i<n;i++){
cin >> a >> op;
if(a[0]=='A'){
tr0=tr0&op;
tr1=tr1&op;
}else if(a[0]=='O'){
tr0=tr0|op;
tr1=tr1|op;
}else if(a[0]=='X'){
tr0=tr0^op;
tr1=tr1^op;
}
}
int p=0;
for(int i=0;(1<<i)<=m;i++){ p++; }
ans=((tr0>>p)<<p);
p--;
bool bn0=false;
//扫描一遍
for(int i=p;i>=0;i--){
if((tr0>>i)&1){
ans=ans|(1<<i);
if((m>>i)&1) bn0=true;
}else if((tr1>>i)&1){
if((m>>i)&1 || bn0){
ans=ans|(1<<i);
}
}else if((m>>i)&1) bn0=true;
}
cout << ans;
return 0;
}
```
#### 时间复杂度
O(n)线性的复杂度!解完只要几毫秒