首页 > 试题广场 >

或与加

[编程题]或与加
  • 热度指数:5218 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解

给定 x, k ,求满足 x + y = x | y 的第 k 小的正整数 y 。 | 是二进制的或(or)运算,例如 3 | 5 = 7。

比如当 x=5,k=1时返回 2,因为5+1=6 不等于 5|1=5,而 5+2=7 等于 5 | 2 = 7。


输入描述:

每组测试用例仅包含一组数据,每组数据为两个正整数 x , k。 满足 0 < x , k ≤ 2,000,000,000。



输出描述:

输出一个数y。

示例1

输入

5 1

输出

2
D_L头像 D_L
先上代码:
#include <iostream>
#include <bitset>
using namespace std;

int main() {
    unsigned long long x, y = 1, k;
    cin >> x >> k;
    
    std::bitset<64> xbs(x), kbs(k);

    for (size_t i = 0, kpos = 0; i < xbs.size(); ++i) {
        if (! xbs.test(i)) { // xbs[i] == 0
            xbs.set(i, kbs[kpos++]);
        }
    }

    y = xbs.to_ullong();
    y ^= x;

    cout << y << endl;    
    
    return 0;
}

再来分析:

此题很容易用代码描述,
bool is_eq(x, y) {
return x + y == x | y;
}

然后整个循环从 1 到 y,y 是 第 k 个 满足 is_eq() 的数。这样做没错,但是 测试用例给整个:
x = 1073741802, k = 1073741823 这么大的数,显然暴力穷举就不合适了。

但是可以举几个数字组合来找其中的规律:
例如:
k = 1 时,5 + 2 == 5 | 2
k = 2 时,5 + 8 == 5 | 8
k = 3 时,5 + 10  == 5 | 10
k = 4 时,5 + 16  == 5 | 16
k = 5 时,5 + 18  == 5 | 18



转二进制


满足这个运算规律 x + y == x | y 的二进制有:
0 + 0 == 0 | 0
1 + 0 == 1 | 0
1 + 1 !=  1 | 1 (只有这个不满足)

所以 x y 各自相对应的二进制位不能同时为 1,换言之, x 中 当前位 为 1 时, 与之对应的 y 那一位 肯定是 0
所以 x 位为 1 的就确定了,可以去除1

X

Y


将 Y 中红色 的 0 去掉看看,得到一组新数据


这正是 从 1 2 3 4 5 6 7,由于 y 表是按照 k 从1递增的顺序得到的值。所以你有理由猜想 这组新数据正是 k !

X  Y K 之间 有了这个关系,就大胆的编写代码去验证吧。

算法大概是,将 x 和 y 都转成 二进制串, 然后将 y 的二进制串依次塞进 x 串中为 0 的部位,得到的一个新值,
把这个值中原先 x 为 1 的 位 都给改成 0,就能得到 y 值。

比如 k = 3 = b(1 1), x = 5 = b(0101)
第一步将 k 塞入 x, 得到 b(1111), 第二步将原先 x 中为 1 的变成 0, 得到 b(1010) , 即 y = 10
编辑于 2016-08-31 16:18:20 回复(8)
import java.util.*;
import java.math.BigInteger;
public class Main{
	public static void main(String args[]){
		Scanner sc=new Scanner(System.in);
        while(sc.hasNext()){
			int x=sc.nextInt();
            int k=sc.nextInt();
            StringBuilder res=new StringBuilder();
            String k_bin=Integer.toString(k, 2);
            int index=k_bin.length()-1;
            while(k!=0){
				if((x&1)==0){
                    res.append(k_bin.charAt(index--));
                    k/=2;
                }else{
                    res.append("0");
                }
                x>>>=1;
            }
            BigInteger num=new BigInteger(res.reverse().toString(), 2);
            System.out.println(num);
        	sc.nextLine();
        }
    }
}
如果或要和加的运算结果相同,i在x的为0的位上可以是0,也可以是1;在x的为1的位上只能是0。所以x的二进制表示为0的位就是可用的“数位”,可以将k的二进制直接“映射”过去。而为1的位只能是0。这样就构建出了结果的二进制表示字符串。
编辑于 2016-04-29 15:55:31 回复(1)
依据一楼大神的版本,以下是java版,并添加了注释
import java.util.Scanner;
public class Main{ 
    public static void main(String[] args) {
        
        Scanner scan = new Scanner(System.in);    
        while(scan.hasNext()){
            long x = scan.nextInt();
            long k = scan.nextInt();
            long bitNum = 1; //指向x的当前位
            long ans =0 ;   //y的各位的值累加,累加完毕得到y
            while( k >0){
                if( (x & bitNum )== 0){   //如果当前x位为0
                    ans += (bitNum*(k & 1)); //则将k的最后一位取出来与填入x对应的为零的位并累加这一位的值
                    k = k >> 1;         //k取出最后一位,即向右移一位
                }
                //上面无论x的当前位为0或1,bitNum都向左移一位
                    bitNum = bitNum << 1;
            }
            System.out.println(ans);//最后输出ans,即是y的值。
        }
    }
}

发表于 2017-10-16 21:13:41 回复(1)
#include <iostream>
#include <vector>
using namespace std;
int main(){
	int x,k;
 	while(cin>>x>>k){
 		long i=1,R=0,n=0;
 		vector<long>vec;
		vector<long> v; 
    	while(k!=0){ 
    		v.push_back(k%2); 
    		k=k/2;
            n++;
 		} 
    while(n>0){ 
    	if(!(x&i)){
     		vec.push_back(i);
            n--;
    	}
 		i=i<<1;
    } 
    for(i=0;i<vec.size();i++){ 
    	if(v[i]==1) 
    		R+=vec[i]; 
    	} 
    	cout<<R<<endl;
    } 
    return 0;
}
编辑于 2016-09-02 09:36:27 回复(1)
pcW头像 pcW
#include <iostream>
#include <bitset>
using namespace std;
 
int main() {
    unsigned long long x, y = 1, k;
    cin >> x >> k;
	
    unsigned long temp = ~x;

    y = 0;
    int ct=0;
    while(temp){
        if(temp & 0x01){
            y|=(k & 0x01)<<ct;
            k >>= 1;
        }
        temp>>=1;
        ct++;
    }

    cout << y << endl;   
     
    return 0;
}
在x二进制位中找0的位置C,然后把k的二进制位按顺序呢填到y的C上。
编辑于 2016-06-21 13:37:47 回复(0)
参考不用加减法做加法的题目
x+y = (x&y) << 1 + x^y
x+y = x|y
x|y = x&y + x^y
解上面的方程可得x&y=0

 
#include <bits/stdc++.h>
 
using namespace std;
 
int main() {
    long long x, k, i = 0, res = 0;
    cin >> x >> k;
    while (k) {
        while ((x&(1ll<<i)) != 0) ++i;
        if (k&1) res += (1ll<<i);        
        k >>= 1;
        ++i;
    }
    cout << res << "\n";
}

发表于 2020-12-06 16:28:22 回复(0)
题意
给定两个正整数 x, k (0 < x, k < 2 0000 0000),求第k个满足 x + y = x | y 的 y (正整数)

思路
要使 x + y = x | y 则 x 和 y 的二进制表示1和1之间的位置应该是不重合的,即 x & y = 0
则把k的二进制表示依次对应到x的二进制表示上为0的位置,其余空位添上0则为答案

举例 n = 5, k = 3
5 -> 0b 0101
3 -> 0b 0011
ans  0b 1010
(将k的1依次放到n上0的位置,其余空位添上0)

注意
由于x,k最大范围为2亿,因此极限情况为 x = k = 2 ^ 30 - 1 = 0b111...1111(连续30个1),则最终答案为0b111...11100000...0000(30个1接30个0),60位,因此最终结果需要用long表示。
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int x = input.nextInt();
        int k = input.nextInt();
        StringBuilder sb = new StringBuilder();
        while (k > 0) {
            if ((x & 1) == 1) {
                sb.append(0);
            } else {
                sb.append(k & 1);
                k >>= 1;
            }
            x >>= 1;
        }
        long ans = Long.parseLong(sb.reverse().toString(), 2);
        System.out.println(ans);
    }
}
发表于 2018-03-24 12:44:58 回复(0)
wa2头像 wa2
#include<iostream>
using namespace std;
int main(){
    long x,k,y,bx,bk;
    while(cin>>x>>k){
        for(bx=bk=1,y=0;bk<=k;bk<<=1){
            while(x&bx)bx<<=1;
            if(k&bk)y|=bx;
            bx<<=1;
        }
        cout<<y<<endl;
    }
    return 0;
}

发表于 2017-09-14 20:39:12 回复(0)
496头像 496
#include<iostream>
using namespace std;
int main(){
    long long x,k;
    cin>>x>>k;
    long long res = x;
    long long i = 1;
    long long x_origin = x;
    while(k){
        if((x&1)==0){
            res += i*(k&1);
            k >>= 1;
        }
        i = i*2;
        x = x>>1;
    }
    cout<<res-x_origin;
}

发表于 2017-03-04 21:31:23 回复(0)
import java.util.Scanner;

public class JRTT3 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scanner = new Scanner(System.in);
		while (scanner.hasNext()) {
			long x = scanner.nextLong();
			long k = scanner.nextLong();
			System.out.println(getNum(x,k));
		}
	}
	public static long getNum(long x, long k) {
		long bitNum = 1;
		long num = 0;
		//目标是把k的各位依次填在x中是0的位上
		//bitNum用来移动到x中零的位置,然后把k的最低位放在x的零位上, k左移,将下一位变成最低位,bitNum一直左移,
		//直到x中的下一个为0的位上。
		while(k!=0){
			if((x & bitNum) == 0){//x中当前bitNUM为0的话,把k的最低位放在这儿
				//k&1是将k的最低位取出来, bitNum*(k&1)的结果就是得到bitNum位和当前k的最低位一样的一个数,而其它位都是0
	            //而num原来的bitNum为肯定为0,num+(bitNum*(k&1)) 就将k的最低位放在x的这个零上了。
				num += (bitNum*(k & 1));
				k >>= 1;
			}
			bitNum <<= 1; //bitNum的1一直左移到x中第k个零的位置
		}
		return num;
	}
	/*public static long getNum(long x, long k) {
		long kMin = 1;
		while (k != 0) {
			if (isOK(x, kMin)) {
				k--;
			}
			kMin++;
		}
		return --kMin;
	}

	public static boolean isOK(long x, long y) {
		return (x + y) == (x | y);
	}*/
}


发表于 2016-09-30 23:32:02 回复(0)
#include <stdio.h>
#include <math.h>

 
int main()
{
    long x,k;
    while(scanf("%ld%ld",&x,&k)!=EOF)
    {
        int index_0[1000];
        long y=0;
        int digit=0,count=0;
        int i=0,j;
        while(x!=0)
        {
            if(x%2 == 0)
            {
                index_0[i] = digit;
                i++;
            }
            digit++;
            x = x/2;
        }
        for(j=i;j<=100;j++)
        {
            index_0[j] = digit;
            digit++;
        }
        while(k!=0)
        {
            if(k%2 == 1)
            {
                y = y+(long)pow(2,index_0[count]);
            }
            count++;
            k = k/2;
        }
        printf("%ld\n",y);
    }
    return 0;
}

发表于 2016-04-29 11:16:59 回复(0)





#include <iostream>

using namespace std;

int main()
{
	long long x, k;
	cin>>x>>k;
	long long bitNum = 1;
	long long ans = 0;
	while(k)
	{
		if((x & bitNum) == 0)
		{
			ans += (bitNum * (k & 1));
			k >>= 1;
		}
		bitNum <<= 1;
	}
	cout<<ans<<endl;
	return 0;
}
应该没有更简单的解法了
-----解法说明------
x+y=x|y
这里可以推出一个结论,x&y=0。也就是说,在二进制上看,x取1的地方,y必定不能取1。从最低位考虑,若x与y在某一位上同时取1,则x+y在该位上为0,x|y在该位上为1,前面说这是最低一位x y同时取1,也就是说没有更低位加法的进位,所以这里两个结果不相等,出现了矛盾。
例子:
x = 001010
y = 110110
x + y =  1000000
x | y = 111110
偏差产生的原因是倒数第二位,x+y=0 x|y=1 且倒数第一位加法没有进位
结论:x在二进制取1的位上,y不能做出改变,只能取0
----方法----
有了上述结论,可以进一步推出只要在x取0的地方,y可以做出改变
例如
x = 10010010011
y = 00000000(0)00   k = 0
y = 00000000(1)00   k = 1
y = 0000000(1)(0)00 k = 2
y = 0000000(1)(1)00 k = 3
y = 00000(1)0(0)(0)00 k = 4
y = 00000(1)0(0)(1)00 k = 5
...
注意观察括号里的数,为x取0的比特位,而如果把括号里的数连起来看,正好等于k。
得出结论,把k表示成二进制数,填入x取0的比特位,x取1的比特位保持为0,得到y
---代码说明---
思路有了,接着就是代码,显然用位操作是最合适的方式。
循环的思想是每次取得k的最低一位,填入到低位开始,x中比特位为0的位置上。
所以用while来判断k是否大于0,若是,说明k还未完全填完
循环体内,需要找到x当前可以填的位置,我们用bitNum来从右往左扫描x的每一位
(x & bitNum) == 0 说明x该位为0,可以把k的当前最后一位填入,用 (k & 1) 取出最后一位,用 ans += (bitNum * (k & 1)) 把k的最后一位填入到当前bitNum指向的位置。
填完后,k右移一位,去掉已经被填过的最后一位,bitNum也向左走一位,避免重复填入x的某个位置。
若x的某个位置为1,则跳过该位置,向左走一位并观察是否可以填入。
两次bitNum向左走一位,合并成一句 bitNum <<= 1;

编辑于 2016-08-27 15:01:17 回复(14)
注释了下一个大神的code,大家一块讨论
#include <iostream>
using namespace std;
int main()
{
	long long x, k;
	cin >> x >> k;
	long long bitNum = 1;
	long long ans = 0;
	//目标是把k的各位依次填在x中是0的位上
	//bitNum用来移动到x中零的位置,然后把k的最低位放在x的零位上, k左移,将下一位变成最低位,bitNum一直左移,知道x中的下一个为0的位上。
	while (k)
	{
		if ((x & bitNum) == 0) //x中当前bitNUM为0的话,把k的最低位放在这儿
		{
			ans += (bitNum*(k & 1)); //k&1是将k的最低位取出来, bitNum*(k&1)的结果就是得到bitNum位和当前k的最低位一样的一个数,而其它位都是0
			//而ans原来的bitNum为肯定为0,ans+(bitNum*(k&1)) 就将k的最低位放在x的这个零上了。
			k >>= 1;
		}

		bitNum <<= 1; //bitNum的1一直左移到x中第k个零的位置
	}
	cout << ans << endl;
	return 0;
}

发表于 2016-08-11 22:32:38 回复(0)
Python 3

x, k = map(int, input().split())
bit = 1
ans = 0
while k:
    if x & 1 == 0:
        ans += bit*(k&1)
        k >>= 1
    x >>= 1
    bit <<= 1
print(ans)

编辑于 2018-08-24 15:59:53 回复(1)
import java.util.*;
public class Main{
    private static int[] getBits(int x){
        int[] bits=new int[32];
        for(int i=0;i<32;++i){
            bits[i]=x%2;
            x>>=1;
        }
        return bits;
    }
    
    private static long bitsToLong(int[] bits){
        long res=0;
        for(int i=bits.length-1;i>=0;--i){
            res=(res<<1)+bits[i];
        }
        return res;
    }
    
    public static void main(String[] args){
         Scanner input=new Scanner(System.in);
         int x=input.nextInt(),k=input.nextInt();
         int[] bits_x=getBits(x),bits_k=getBits(k);
         int[] bits_res=new int[64];
         int i=0,j=0,m=0;
         for (;i<32;++i){
            if(bits_x[i]==0){
                bits_res[m++]=bits_k[j++];
            }else{
                bits_res[m++]=0;
            }
         }
         for (;j<32;++j)
            bits_res[m++]=bits_k[j];
         System.out.println(bitsToLong(bits_res));
    }
}
发表于 2022-04-01 12:15:11 回复(0)
将k、x都化为二进制
如k=3则化为二进制k为"101",表示二进制的x从后往前的第1个为0的位应该改为1,第3个为0的数应该改为1;
如k=6则化为二进制k为"110",表示二进制的x从后往前的第2个为0的位应该改为1,第3个为0的数应该改为1;
最后得到的tempStr即为x+y的二进制字符串,这时只需要将其转成long再减去原来的x即可得到y

import java.util.Scanner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main{
    public static void main(String args []){
        Scanner scanner = new Scanner(System.in);
        String str = scanner.nextLine().toString();
        String arrs[] = str.split(" ");
        long x=0;
        long k=0;
        x = Long.parseLong(arrs[0]);
        k = Long.parseLong(arrs[1]);
        String[] strX = Long.toBinaryString(x).split("");
        String[] strK = Long.toBinaryString(k).split("");
        List<String> strXList = new ArrayList<String>(Arrays.asList(strX));
        List<String> strKList = new ArrayList<String>(Arrays.asList(strK));
        int i = 0;
        boolean over = false;
        int j = 0;
        for(i = strKList.size() - 1;i>-1;i--){
            while (!over && j< strXList.size()){
                int index = strXList.size() - j - 1;
                if (!strXList.get(index).equals("0")){
                    j++;
                    continue;
                }else {
                    strXList.set(index,strKList.get(i));
                    j++;
                    break;
                }
            }
            if (j==strXList.size()){
                over = true;
            }
            if (over){
                strXList.add(0,strKList.get(i));
                continue;
            }
        }

        String tempStr = "";
        for (String s : strXList) {
            tempStr+=s;
        }
        long y = Long.valueOf(tempStr,2) - x;
        System.out.println(y);
    }
}


发表于 2021-09-04 20:28:52 回复(0)
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
int main(){
    long long x,k,ans=0,bitnum=1;
    cin>>x>>k;
    while(k!=0){
        while(bitnum&x)
            bitnum*=2;
        ans+=bitnum*(k%2);
        bitnum*=2;
        k/=2;
    }
    cout<<ans;
    return 0;
}
必须向大佬献上我的瑞斯拜。另,左移右移可以通过乘二除二完成,取末位可以通过模二完成,不一定要通过位操作,忘了的话可以用普通的运算代替,但是对某些题目说不定会时间不够。
发表于 2021-08-22 17:03:18 回复(0)
m, k = map(int, input().split()) a = [] for i in range(m*k): if m+i == (m|i): a.append(i) print(a[k-1]) python才是永远的神
发表于 2021-03-05 23:01:31 回复(0)
import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        long x = scanner.nextLong();
        long k = scanner.nextLong();
        long t = ~x;
        long mask = 1;
        for(int i=0;i<64;++i, mask<<=1){
            if((mask & t)==0)continue;
            if( (k&1)==0){
                // clear ith bit
                t &= (~mask);
            }
            k>>>=1;
        }
        System.out.println(t);
    }
}


发表于 2020-03-01 15:56:10 回复(0)
import java.math.BigInteger;
import java.util.*;

public class Main{
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        try {
            while (scan.hasNext()) {
                long x = scan.nextLong();
                long k = scan.nextLong();
                findY(x, k);
            }
        } finally {
            scan.close();
        }
    }
    private static void findY(long x, long k) {
        String xStr = Long.toBinaryString(x);
        String kStr = Long.toBinaryString(k);
        StringBuilder y = new StringBuilder();
        int xPos = xStr.length()-1;
        int kPos = kStr.length()-1;
        while (xPos >=0 || kPos >=0) {
            if (xPos >=0 && xStr.charAt(xPos) == '1') {
                y.append("0");
            } else {
                y.append(kPos >=0 ? kStr.charAt(kPos) : '0');
                kPos--;
            }
            xPos--;
        }
        System.out.print(new BigInteger(y.reverse().toString(), 2));
    }
}
发表于 2019-03-13 17:31:36 回复(0)