CF769 - C. Strange Test(思维,位运算)

C. Strange Test

题意:

给出两个数 a, b。 ( 1 ≤ a < b ≤ 1 0 6 ) (1≤a<b≤10^6) (1a<b106)
每次执行下面一种操作,问最少经过多少次操作能够使得 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 (aa)+(bb)+(abb)+1
化简后发现,只与 a'+a'|b' 的值有关。
因为 a ′ a' a 最大不超过 1e6,所以可以遍历 a ′ a' a 的值。对于每个 a ′ a' a 的值,找到能使 a'|b' 最小的 b ′ b' b,不断更新答案。

也就是说,b’ 需要满足两个条件:

  1. 要大于等于 b;
  2. 要尽可能使得 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’,然后写出来式子,枚举其中的一个值求另一个值让整个式子的值最小。

又看了一遍挺有收获的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值