问题描述
设机器字长 w w w比特, u ∣ w , w = u c u|w,w = uc u∣w,w=uc。将 w w w分为 c c c个连续长度为 u u u比特的区间,称为 u u u区间。实现操作:对于一个机器字 x x x,如果一个 u u u区间非0,则将区间最右bit置1,其余为0。要求算法时间为 O ( 1 ) O(1) O(1)
解
思路
考虑异或操作。对于一个
u
u
u位的二进制数
x
x
x,当且仅当
x
=
0
x = 0
x=0的时候,
f
(
x
)
=
(
(
N
O
T
(
x
)
)
+
1
)
f(x) = ((NOT(x))+1)
f(x)=((NOT(x))+1)会向第u+1位进位。其中
N
O
T
(
x
)
NOT(x)
NOT(x)表示对
x
x
x取反
于是,当
c
=
1
c=1
c=1的时候,即
u
u
u区间就是字长时,通过执行
f
(
x
)
f(x)
f(x)后
C
F
CF
CF位是否为1,可判断
x
x
x是否为0。
但是,当
c
>
1
c>1
c>1时,我们就必须考虑上述的“进位操作”能否正常的保存下来,以便于我们判零。
不妨先考虑
c
=
2
c=2
c=2时的情况,设高地址的
u
u
u区间为
x
x
x,低地址的
u
u
u区间为
y
y
y,
若
y
y
y不产生进位,我们就无需考虑进位之后的问题;
若
y
y
y产生了进位,那么什么时候这种进位可以在
x
x
x的最低位保存下来呢?显然是
x
x
x的最低位为0时(取反再加1后,该位为0)。
如果
x
x
x的最低位不为零怎么办?我们可以先处理它,得到最低位对答案的贡献,记为
a
n
s
1
ans_1
ans1。然后将最低位统统清零,再通过
f
(
x
)
f(x)
f(x)来判零。记此时的答案为
a
n
s
2
ans_2
ans2
可是又产生了一个新的问题:如果一个区间的值就是00…01,在最低位清零之后,
f
(
x
)
f(x)
f(x)判零法会失效。怎么办?
我们先枚举某个区间在两次答案中的所有可能情况
S
S
S={(零,非零),(零,零),(非零,零),(非零,非零)}
首先,(零,非零)不会出现,可以直接排除;
其次,对于(零,零) 和 (非零,非零),判零法是有效的;
最后,出现(非零,零),说明判零法失效,我们需要消除这种影响。
对于
c
>
2
c>2
c>2的情况可以直接推广。
过程
分析完解题思路后,假设我们得到的
w
w
w位二进制数是
x
x
x,
我们如何来得到
a
n
s
1
ans_1
ans1呢?
对
x
x
x按位与一个二进制常数
c
1
=
(
(
0
)
u
−
1
1
)
c
(
2
)
c_1={((0)^{u-1}1)^c}_{(2)}
c1=((0)u−11)c(2),(
i
i
i的
k
k
k次幂表示连续的
k
k
k个
i
i
i)
常数中的1用来“保存”我们需要的位,如果那一位是1,则会被记录到
a
n
s
1
ans_1
ans1的相应位中,否则就不会被记录下来。
之后,如何消除最低位呢?
对
x
x
x按位与一个二进制常数
c
2
=
(
(
1
)
u
−
1
0
)
c
(
2
)
c_2={((1)^{u-1}0)^c}_{(2)}
c2=((1)u−10)c(2),并替换
x
x
x
此时,常数中的0负责对相应的位清零
再然后,对
f
(
x
)
f(x)
f(x)的结果右移
u
u
u位就可以得到
a
n
s
2
ans_2
ans2了
注:最高位的进位(如果有),保存在CF中,需要采用带进位循环右移一位取出CF到最高位,之后再采用逻辑右移对最高位补零