两个二进制数的最大公因数的一个隐藏关系

今天参加牛客暑期集训时发现了一道关于位运算的题目,题目是

  • Ben_H 有一个正整数 ( x )。
  • 他希望找到一个另一个正整数 ( y ),这个数必须严格小于 ( x )。
  • 他希望满足方程 ( \gcd(x, y) = x \oplus y ),其中 ( \gcd ) 表示最大公约数,( \oplus ) 表示位运算中的异或操作。
  • https://ac.nowcoder.com/acm/contest/81597/Ey

异或就是相同时为0,不同时为1,比如1001异或0100就是1101. 

首先想到的肯定是暴力解法,但肯定会超时,看到有个异或我们就能想到这题多半与位运算有关系。

我们可以想想,假设我们输入的二进制形式的x=10001000100,不妨我们可以看看当y=10001000000时,x与y的关系,他们两个的唯一区别就是x的最后一位1在y中变为了0。

现在我们来试试z=x^y等于多少,^就是异或的意思。

x=10001000100

y=10001000000

————————

z=00000000100

很显然因为我们只改变的x中的一位数赋给y,所以他们的异或值除了改变的那个位为1之外其他位全为0。

现在我们来看看gcd(x,y)等于多少呢,用计算机算一下不难发现gcd(x,y)=6。将z转为十进制后我们发现竟然gcd(x,y)=x^y。现在我们可以得出结论当x的最后一位1变成0赋给y时满足gcd(x,y)=x^y。因为我们只改变了最后一位1,所以我们可以轻易看出十进制中y=x-lowbit(x)。lowbit(x)函数返回的就是二进制x中最后一位1转换为十进制的值。

毕竟这只是结论还缺少证明,所以现证明如下:

总所周知如果两个相邻的数,他们的最大公因数肯定就是1。比如x=7,y=6时gcd(x,y)=1。

现在我们将3与4都转化为二进制x=111,y=110。又总所周知在二进制中我们想要将一个数乘以2是通过将二进制数左移一位。现在我们将x与y都乘以2的5次方,也就是说将x与y都左移五位,便得到x=11100000,y=11000000。欸,现在是不是有点眼熟了,对的,现在的x与y的区别就是x的最后一位1变成了0赋给了y。而他们的最大公因数因双方都乘以了2的5次方,所以最大公因数1也得乘以2的5次方。而我们发现因为异或的性质x^y=二进制100000=2的5次方。也就是lowbit(x)的值。

所以得知以上性质后得出ac代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll lowbit(ll x){
    return x&(-x);
}
void slove(){
    ll x;
    cin>>x;
    if(x==lowbit(x)){//判断x是不是2的幂,假如x是2的幂x=100000,因为题中要求y<x的所以y只能为
        cout<<"-1"<<endl;//0xxxxxx之类的,这时x^y就会使得第一位变为1,后面位无论什么情况都会
        return;//使得x^y>x了。
    }
    cout<<x-lowbit(x)<<endl;
}
int main(){
    int n;
    cin>>n;
    while(n--){
    slove();
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值