GCD=XOR问题(UVA 12716 阉割版)

GCD=XOR问题

问题

给定一个n, 问求出(a,b)无序数对, 使a^b=gcd(a, b)

题解

数学证明

设a>=b, a^b=c

因为^的规则是, 只有两个数的同一位有一个0和一个1时, 结果才为1, 否则都是0.

这样对于a, b, c的每一位的真假, 只可能出现两种情况: 0,0,0和0,1,1. 而这两种情况都是对于a, b, c知二推一.

所以,若 a^b=c, 则 a^c=b, b^c=a.

当a-b的时候, 分类讨论每一位:

某位上, 0-0=0, 这时c的这一位也是0

1-1=0, 这时c的这一位是0

1-0=1,c的这一位是1

0-1=-1,借位,这时c的这一位是1.

所以, 前三种情况结果都是a^b=a-b

只有最后一种情况a^b>a-b.

所以a^b>=a-b.

而且, 又因为c=gcd(a, b)<=a-b (显而易见, 否则a/c, b/c就有可能是分数)

题目规定的c=gcd(a, b)=a^b

c=a-b=a^b就是它的必要条件

设计算法

接下来, 只要保证a-b>c就可以求出所有结果了.

因为a不能等于b, 因为这时gcd(a, b)=a=b, a-b=0

所以a至少比b大c.

又因为b至少是c的一倍

所以a必须是两倍起跳.

又因为a<=n, a>=b+c>=2c.

所以c必须小于等于n/2.

因为a xor b=c, a xor c=b, b xor c=a, 知一推二

a-b=c, a-c=b, 知一推一

所以只要a^c=a-c

就满足a^b=a-b

这样就保证了在这个范围内的a, c, 只要满足a^c=a-c, 答案就成立.

#include<iostream>
#include<cstdio>
using namespace std;
long long N, Ans = 0;
int main()
{
	cin >> N;//设b<a
	for (register int i = 1; i <= N >> 1; i++) {//这个地方枚举的是gcd, 因为gcd一定小于b, 并且gcd=a^b=a-b, 所以gcd一定小于a/2(也就是N/2), 这样gcd就一定小于b了
		for (register int j = i << 1; j <= N; j += i) {//根据gcd枚举a, 因为是倍数关系, 所以增量为gcd, 而且a>b且b最小就是gcd的一倍, 所以a是两倍起跳
			if ((i ^ j) == j - i) {//符合必要条件
				Ans++;
			}
		}
	}
	cout << Ans << endl;//其实只有一个数字不用printf.
	return 0;
}
原文地址:https://www.cnblogs.com/Wild-Donkey/p/12629341.html