C. Strange Test
题意:
给出两个数 a, b。
(
1
≤
a
<
b
≤
1
0
6
)
(1≤a<b≤10^6)
(1≤a<b≤106)
每次执行下面一种操作,问最少经过多少次操作能够使得
a
=
b
a=b
a=b。
a++;
b++;
a |= b;
分析:
对于 取或 操作,执行之后 a 的值就不小于 b 了,那么后面就只需要执行 b++,而用不到 取或 操作了。所以 取或 操作最多只会用一次。
如果不用取或操作:那么操作次数为 b-a
;
否则,用一次取或操作,在什么时候用呢?
设当
a
a
a 变化到
a
′
a'
a′,b 变化到 b’ 时使用,那么操作次数就为:
(
a
′
−
a
)
+
(
b
′
−
b
)
+
(
a
′
∣
b
′
−
b
′
)
+
1
(a' - a) + (b' - b) + (a'|b' - b') +1
(a′−a)+(b′−b)+(a′∣b′−b′)+1。
化简后发现,只与 a'+a'|b'
的值有关。
因为
a
′
a'
a′ 最大不超过 1e6,所以可以遍历
a
′
a'
a′ 的值。对于每个
a
′
a'
a′ 的值,找到能使 a'|b'
最小的
b
′
b'
b′,不断更新答案。
也就是说,b’ 需要满足两个条件:
- 要大于等于 b;
- 要尽可能使得
a'|b'
最小。
可以这样构造:
从高位到低位遍历 a’ 的二进制每一位,如果:
- a’ 当前位为0,b 当前位为1,为了满足条件1,b’当前位要为1;
- a’ 当前位为0,b 当前位为0,为了满足条件2,b’当前位要为0;
- a’ 当前位为1,b 当前位为1,为了满足条件1,b’当前位要赋值为1;
- a’ 当前位为1,b 当前位为0,如果把 b’ 当前位赋值为1的话,那么后面的所有位置都不用考虑条件 1 了,为了满足条件 2,后面所有位置都可以赋值为0。
所以遇到这种情况之后,可以直接break。
这样,便可以求出 a'+a'|b'
的最小值,得到最小操作次数。
Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 200010, mod = 1e9+7;
int T, n, m, k;
signed main(){
Ios;
cin>>T;
while(T--)
{
int a, b;
cin>>a>>b;
int ans = 1e9, t=a;
while(a<b)
{
int b1 = 0;
for(int i=20;i>=0;i--)
{
int x = !!(a & (1<<i)), y = !!(b & (1<<i));
if(x==0 && y==1) b1 |= (1<<i);
if(x==1 && y==1) b1 |= (1<<i);
if(x==1 && y==0){
b1 |= (1<<i);break;
}
}
ans = min(ans, a - t - b + (a|b1) + 1);
a++;
}
cout<<min(ans, b-t)<<endl;
}
return 0;
}
有个思维的地方需要想到最多用一次取或,然后要敢设出未知的修改位置 a’, b’,然后写出来式子,枚举其中的一个值求另一个值让整个式子的值最小。
又看了一遍挺有收获的。