一些关于位运算的奇特性质(持续跟新)
一、异或 ⨁ \bigoplus ⨁
异或,又称不进位加法,或者不借位减法。
性质:
1.
1.
1.满足交换律和结合律
2.
2.
2.
a
⨁
a
=
0
a\bigoplus a =0
a⨁a=0,
a
⨁
0
=
a
a\bigoplus 0=a
a⨁0=a.
3.
3.
3.
i
f
a
⨁
b
=
c
if \ a\bigoplus b = c
if a⨁b=c,
t
h
e
n
a
=
b
⨁
c
then \ a = b\bigoplus c
then a=b⨁c.
2、 3合起来使得 ⊕ \oplus ⊕满足可减性。不过运算时要把减号换成 ⊕ \oplus ⊕。于是我们在实际运算中,完全可以把异或看成加法和减法。这使得它能够做前缀和,以及
Acwing-高斯消元解异或线性方程组。
由于其不进位加法的性质,在异或操作时,对于二进制位上对应的两个数,我们如果要想使其增大,需要尽量选择不一样的;如果想使其减小,那么必须对应位置上都是1和1。
二、与 & \& &
1.
1.
1.满足交换律和结合律
2.
2.
2.
a
&
b
≤
min
(
a
,
b
)
a\&b \le \min(a,b)
a&b≤min(a,b)
这是因为,当我们对一个二进制位上进行&时,答案不会变得更大。
三、或 ∣ | ∣
这个运算用的不多。
不过有一个简单的性质:
a
∣
b
≥
max
(
a
,
b
)
a|b\ge\max(a,b)
a∣b≥max(a,b)。
四、一些神奇的推论
1.和加法的联系
a
+
b
=
a
⊕
b
+
2
a
&
b
=
a
&
b
+
a
∣
b
a+b=a\oplus b +2a\&b=a\&b + a|b
a+b=a⊕b+2a&b=a&b+a∣b
证明:如果不进位,那么结果就是 a ⊕ b a\oplus b a⊕b。考虑进位,就是两个数二进制位上同时为 1 1 1,并且左移一位得到的数。两个相加就可以得到结果。 a & b + a ∣ b a\&b + a|b a&b+a∣b中,两个数二进制位上同时为 1 1 1时则进位,不是则不进位。
这个性质有两个例题:CF672A。
题意:给出两个数
2
≤
s
≤
1
0
12
2\le s \le 10^{12}
2≤s≤1012和
0
≤
x
≤
1
0
12
0 \le x\le 10^{12}
0≤x≤1012,求有序正整数对
(
a
,
b
)
(a,b)
(a,b)有多少对。其中
(
a
,
b
)
(a,b)
(a,b)满足:
a
+
b
=
s
a
⊕
b
=
x
\begin{aligned} &{a+b=s}\\ &{a\oplus b = x} \end{aligned}
a+b=sa⊕b=x
为了方便理解,我们设
d
p
[
i
]
dp[i]
dp[i]为只考虑前
i
i
i位二进制位有多少种方案。
我们容易求出
a
&
b
a\&b
a&b,然后对于
a
&
b
a\&b
a&b和
a
⊕
b
a\oplus b
a⊕b二进制同一位上的两个数,可以发现:
如果
a
&
b
a\&b
a&b的第
i
i
i位为
1
1
1,那么
a
a
a、
b
b
b这两位上必为1。因此,如果
(
a
⊕
b
)
&
(
1
<
<
(
i
−
1
)
)
=
1
(a\oplus b)\&(1<<(i-1))=1
(a⊕b)&(1<<(i−1))=1那么没有答案,否则
d
p
[
i
]
=
d
p
[
i
−
1
]
dp[i]=dp[i-1]
dp[i]=dp[i−1];
如果
a
&
b
a\&b
a&b的第
i
i
i位为
0
0
0,那么当
a
⊕
b
a\oplus b
a⊕b为
1
1
1时,
d
p
[
i
]
=
d
p
[
i
−
1
]
∗
2
dp[i]=dp[i-1]*2
dp[i]=dp[i−1]∗2
,否则
d
p
[
i
]
=
d
p
[
i
−
1
]
dp[i]=dp[i-1]
dp[i]=dp[i−1]。
这个题还有些小坑。比如当
s
<
x
s<x
s<x或者
(
s
−
x
)
%
2
≠
0
(s-x)\%2\ne0
(s−x)%2=0时直接输出
0
0
0,
还有就是,如果定义
1
<
<
35
1<<35
1<<35,那么
1
1
1是
i
n
t
int
int会溢出。我们需要用
1
l
l
<
<
35
1ll<<35
1ll<<35。
最后一个坑点,题目要求的是正整数对,而我们这种考虑方案可能会选到
0
0
0。如果
a
=
0
a=0
a=0,那么
a
&
b
a\&b
a&b必然为
0
0
0。因此当
a
&
b
=
0
a\&b=0
a&b=0时我们需要减去这种方案。
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
using namespace std;
const double eps = 1e-10;
const double pi = acos(-1.0);
const int maxn = 1e6 + 10;
ll s,x;
void solve(){
scanf("%lld%lld",&s,&x);
if((s-x)%2 || s < x){
puts("0");
return;
}
s = (s-x)>>1;//s表示(a&b)
ll t = max(s,x),ans = 1;
int len = 0;
while(t) t>>=1,len++;
for(int i = 1; i <= len; i++){
if((s&(1ll<<(i-1))) && (x&(1ll<<(i-1))) ){
puts("0");
return;
}
if(!(s&(1ll<<(i-1))) && (x&(1ll<<(i-1))) ) ans <<= 1ll;
}
if(s == 0) ans -= 2;
printf("%lld\n",ans);
}
int main()
{
solve();
return 0;
}
2.找最低位(最右边的
1
1
1)
l
o
w
b
i
t
(
x
)
=
x
&
(
−
x
)
lowbit(x)=x\&(-x)
lowbit(x)=x&(−x)
3.取出第
i
i
i位(从右往左)
a
&
(
1
<
<
(
i
−
1
)
)
a\& \left(1<<(i-1)\right)
a&(1<<(i−1))
4.
a
−
1
a-1
a−1会使
a
a
a二进制位上的第一个出现的
1
1
1变成
0
0
0,而这个
1
1
1右边的
0
0
0 全部变成
1
1
1。