CF#769 Strange Test

CF#769 Strange Test

题面

 

 

题目大意

给你两个整数a和b,(1<=a<b<=10^6)。每次可以执行以下三个操作之一,使得a等于b。求最小操作次数。

每次可以执行的三个操作为:

  1. a=a+1

  2. b=b+1

  3. a=a|b

解题思路

  注意到在执行操作3之后,a一定会大于等于b,那么之后要么a等于b,操作结束,要么只能执行操作2,令b=b+1直至b等于a。所以在执行了操作3之后,就只能执行操作2了。于是操作3在整个过程中只能执行一次。

  假设我们在执行操作三之前经过若干次操作1和操作2,a和b分别变成了x和y,那么跟据前面的分析,总操作次数就是 (x-a)+(y-b)+1+(x|y)-y=x+(x|y)+(1-a-b).最后一项是常数,只需最小化x+(x|y).其中x一定小于等于b,因为x=b时候表示只执行b-a操作1使a=b,x>b时是的方案一定不是最优的。

  那么我们可以将x从a到b进行迭代,求最小的x+(x|y)。任一给定的x,我们可以通过以下方法构造y。将x,b都写成2进制数,从高位到低位遍历b的二进制位。

  • 若b当前二进制位是1,x对应的二进制位是1,设y对应的二进制位为1

  • 若b当前二进制位是1,x对应的二进制位是0,设y对应的二进制位为1

  • 若b当前二进制位是0,x对应的二进制位是0,y设y对应的二进制位为0

  • 若b当前二进制位是0,x对应的二进制位是1,设y对应的二进制位为1,结束,跳出循环

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
#define ull unsigned long long
using namespace std;
int main(){
   ios::sync_with_stdio(false); cin.tie(0);
   //freopen("in","r",stdin);
   //freopen("out","w",stdout);
   int T;
   cin>>T;
   while(T--){
       int a,b;
       cin>>a>>b;
       int ans=1<<21;
       for(int x=a;x<=b;x++){
           int y=0;
           int pos=1<<20;//保证大于10^6
           while(pos){
               if(b&pos){
                   y|=pos;
              }
               else if(x&pos&&!(b&pos)){
                   y|=pos;
                   break;
              }
               pos>>=1;
               //cout<<pos<<endl;
          }
           ans=min(ans,x+(x|y));
      }
       cout<<min(ans+1-a-b,b-a)<<endl;
  }
   return 0;
}

 

posted on 2022-02-10 18:37  _epiphany  阅读(152)  评论(0)    收藏  举报