【积累】关于与或异或的一个积累
关于与或异或的积累
1,例题1
题意:
给 \(n\) 个操作: op a
,有 \(3\) 种:
- \(op_i=1\) ,表示将 \(x\) 与 \(a_i\) 做与运算;
- \(op_i=2\) ,表示将 \(x\) 与 \(a_i\) 做或运算;
- \(op_i=3\) ,表示将 \(x\) 与 \(a_i\) 做异或运算。
要求将 \(n\) 个操作缩小成 小于等于 \(5\)个操作,使得对于任意 \(x\) ,进行这 \(n\) 个操作后的结果与 进行这 \(m\) 个操作的结果一致。
其中 \(0\leq a_i\leq2^{20}-1\) 。
解:
先分别算出 \(x_i=0\) 与 \(x_i=1\) 在这 \(n\) 个运算之后的结果。
对 \(x\) 设置 \(3\) 种运算,例如 ((x&a)^b)|c
或者 ((x&a)|b)^c
等等;
将二进制位的每一位独立开来看,然后对于 \(x_i=0\) 与 \(x_i=1\) 进行 \(n\) 种运算之后的结果,设置对应的运算数值。
例如 设置的运算时 ((x&a)^b)|c
,那么,
当 \(x_i=0\) 与 \(x_i=1\) 进行 \(n\) 种运算之后的结果分别是 \(0\) , \(0\) ,可以设 \(a_i=0\) ,\(b_i=0\) ,\(c_i=0\) ;
当 \(x_i=0\) 与 \(x_i=1\) 进行 \(n\) 种运算之后的结果分别是 \(0\) , \(1\) ,可以设 \(a_i=1\) ,\(b_i=0\) ,\(c_i=0\) ;
当 \(x_i=0\) 与 \(x_i=1\) 进行 \(n\) 种运算之后的结果分别是 \(1\) , \(0\) ,可以设 \(a_i=1\) , \(b_i=1\) ,\(c_i=0\) ;
当 \(x_i=0\) 与 \(x_i=1\) 进行 \(n\) 种运算之后的结果分别是 \(1\) , \(1\) ,可以设 \(c_i=1\) ;
其余同理。
代码:
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define mkp(a,b) make_pair(a,b)
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int a[22],b[22];
int main()
{
for(int i=0;i<20;i++)a[i]=1;
int n,op,x,t,c;
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&op,&x);
for(int i=0,c=0;i<20;i++,x>>=1,c++)
{
t=x&1;
if(op==1)a[c]&=t,b[c]&=t;
else if(op==2){
a[c]|=t;b[c]|=t;
}else{
a[c]^=t;b[c]^=t;
}
}
}
x=0;int y=0,z=0;
for(int i=0,t=1;i<20;i++,t<<=1)
{
if(a[i]==0&&b[i]==0);
else if(a[i]==1&&b[i]==0)x|=t;
else if(a[i]==0&&b[i]==1)x|=t,y|=t;
else if(a[i]==1&&b[i]==1)z|=t;
}
printf("3\n1 %d\n3 %d\n2 %d\n",x,y,z);
}
2,例题2
题意:
给定 \(n\) 和 \(m\) ,\(2\le n \le10^5,0\le m\le 10^9\) , $ x \le m$
有 \(n\) 个操作 :op a
, 有 \(3\) 种:
- \(op_i=AND\) ,表示将 \(x\) 与 \(a_i\) 做与运算;
- \(op_i=OR\) ,表示将 \(x\) 与 \(a_i\) 做或运算;
- \(op_i=XOR\) ,表示将 \(x\) 与 \(a_i\) 做异或运算。
询问最后可得到的最大值是多少。
解:
先分别算出 \(x_i=0\) 与 \(x_i=1\) 在这 \(n\) 个运算之后的结果。
答案要求最大,那么就是尽可能先让最终值的最高位为1:
当 \(x_i=0\),而终值是 \(1\) 时 ,可以直接让答案 + \(1<<i\),因为 \(x_i=0\) ,不会影响 \(x\) 与 \(m\) 的关系;
否则,如果 \(x_i=1\) ,而终值也是 1,且对于当前 \(m\) , \(m\ge (1<<i)\) ,则让答案+ \(1<<i\) ,且让 \(m-=1<<i\) 。(贪心)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
char s[5];
int main()
{
int n,m,x=0x7fffffff,y=0,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s%d",s,&v);
if(s[0]=='A')x&=v,y&=v;
else if(s[0]=='O')x|=v,y|=v;
else x^=v,y^=v;
}
int res=0;
for(int i=30;i>=0;i--){
if(y&(1<<i))res+=1<<i;
else if(x&(1<<i)&&m>=(1<<i))m-=1<<i,res+=1<<i;
}
printf("%d\n",res);
}