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

C. Strange Test

题意:

给出两个数 a, b。 ( 1 ≤ a < b ≤ 1 0 6 ) (1≤a(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,为了满足条件,b’当前位要赋值为1;
  • a’ 当前位为0,b 当前位为0,为了满足条件,b’当前位要赋值为0;
  • a’ 当前位为1,b 当前位为1,为了满足条件,b’当前位要赋值为1;
  • a’ 当前位为1,b 当前位为0,b’当前位赋值为1的话,在满足第二个条件的前提下,还将使得后面的所有位置都不需要考虑第一个条件,后面所有位置都可以赋值为0。所以直接break。

这样,便可以求出 a'+a'|b' 的最小值,便能得到最小操作次数。

Code:

#include
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;
}

你可能感兴趣的:(codeforces,CF)