C. Interesting Sequence

传送门

题意:给你一个n和x,问你是否存在一个m,使

n&(n+1)&(n+2)&...&m=x如果存在输出最小的m,否则输出-1。

思路:因为是与运算,那么与之后的结果一定不会大于n,所以如果m>n就可以直接输出-1.

而且数据范围比较大,那么我们可以把n转化为二进制数。

假如是90转换为二进制数就是1011010,

&n+1即1011010&1011011=1011010

&n+2即1011010&1011100=1011000

&n+3即1011000&1011101=1011000

&n+4即1011000&1011110=1011000

&n+5即1011000&1011111=1011000

&n+6即1011000&1100000=1000000,

好那么到这里我们可以发现,每次与的结果只能为从高位开始的1的连续子区间的前缀

(比如1011010可以得到1011000但是不能得到1000010(如果要前面的1为0那么后面的1一定会提前为0)

也不能得到1010000(如果要第三个位置为1第四个为0那么与的数第四个位置也要为0,但是如果为0那么就会进位导致第三位置的1也会0)

并且,而且二进制数如果x中为1但是n不为1那么无论怎么加也不能使当前位为1.

于是不能得到的情况就处理完了,现在就看如何得到最小的m。

那么我们也可以从上图发现,最小的m就是从高到低起,x的二进制位和n的二进制不同的i前面一位(比如要得到1000000,那么与1011000不同位为第3位,那么就要往前挪一位&1100000就可以得到)。

代码:


#include<cstdio>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include<vector>
#include<queue>
#include<map>
#define sc_int(x) scanf("%d", &x)
#define sc_ll(x) scanf("%lld", &x)
#define pr_ll(x) printf("%lld", x)
#define pr_ll_n(x) printf("%lld\n", x)
#define pr_int_n(x) printf("%d\n", x)
#define ll long long 
using namespace std;

const int N=1000000+100;
ll n ,m,h;
int a[N],b[N];

void solve()
{
    for(int i =1;i<=70;i++)//初始化
    a[i]=b[i]=0;
    ll x,y;
    cin>>x>>y;
    n=x,m=y;

    if(m>n)
    {
        cout<<"-1\n";
        return ;
    }    
    if(m==n){
        cout<<m<<endl;
        return ;
    }
    int sizea=1,sizeb=1;
    for(int i =1;i<=70;i++)//从小到大转换为二进制到数组里面
    {
        if(n%2){
            a[i]=1;
            sizea=i;
        }
        if(m%2){
            b[i]=1;
            sizeb=i;
        }
        n/=2,m/=2;
    }    

    bool ii=0;
    for(int i =max(sizea,sizeb);i>=1;i--)
    {
        if(a[i]!=b[i])
        {
            if(b[i]){
            cout<<"-1\n";
            return ;                    
            }
            else  ii=1;//前缀只能到这个位置
        }
        else{
            if(a[i]==1&&ii) //后面有1并且前面有0了
            {
            cout<<"-1\n";
            return ;                    
            }
        }
    }


    for(int i =max(sizea,sizeb);i>=1;i--)//找连续的1的区间
    {
        int j=i;
        if(a[j]==1){
            int sum=0;
            while(a[j]==1)
            {
                if(b[j]==0&&j>=1) sum++;
                j--;
            }
            if(sum!=i-j&&sum!=0){
            cout<<"-1\n";
            return ;                    
            }
        }
        i=j;
    }

    for(int i =max(sizea,sizeb);i>=1;i--)//从最高位找不同的
    {
        if(a[i]!=b[i]&&a[i]==1)
        {
            b[i+1]=1;
            break;
        }
    }


    ll res=0,t=1;
    for(int i =1;i<=70;i++)    
    {
        if(b[i]==1)
        res+=t;
        t*=2;
    }
    cout<<res<<endl;

    
    return ;
}


int main()
{
    int t;
    sc_int(t);
    while(t--)solve();
    
    return 0;
}

/*

101010 
011111
1010 1011 1100 1101 1110 1111

1010
1000

*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值