【UVA12716】GCD XOR——杨子曰题目

【UVA12716】GCD XOR——杨子曰题目

Given an integer N, find how many pairs (A, B) are there such that: gcd(A, B) = A xor B where1 ≤ B ≤ A ≤ N.
Here gcd(A, B) means the greatest common divisor of the numbers A and B. And A xor B is the value of the bitwise xor operation on the binary representation of A and B.
Input
The first line of the input contains an integer T (T ≤ 10000) denoting the number of test cases. The
following T lines contain an integer N (1 ≤ N ≤ 30000000).
Output
For each test case, print the case number first in the format, ‘Case X:’ (here, X is the serial of the
input) followed by a space and then the answer for that case. There is no new-line between cases.
Explanation
Sample 1: For N = 7, there are four valid pairs: (3, 2), (5, 4), (6, 4) and (7, 6).
Sample Input

2
7
20000000

Sample Output

Case 1: 4
Case 2: 34866117

一个草率的翻译:给你一个n,让你求出有多少对(a,b)满足:1 ≤ b ≤ a ≤ n且 gcd(a,b)=a xor b


啊!这道题的n真的好大,不过它的时限是5s,哈哈哈哈哈,那就好办了呀! n 2 n^2 n2暴力枚举a和b然后再用 l o g n logn logn检验一下——复杂度 O ( n 2 l o g n ) O(n^2log n) O(n2logn),大哥5s也不是让你乱搞吧!
我们令c=gcd(a,b)=a xor b
首先我们会发现同时枚举a和b是不现实的,So,我们就枚举一个,假设是a好了,确定了a然后就没有然后了,那我们可以怎么办呢?我们可以再枚举一个c,也就是 g c d ( a , b ) gcd(a,b) gcd(a,b),也就是枚举a的因子,这样我们只需要 O ( n   l o g   n ) O(n \ log \ n) O(n log n)的时间枚举出所有的a和它的因子,哦那还蛮优的,我们又知道c=a xor b,于是乎,我们就可以把b解出来,b=a xor c,BUT这时的b不一定是答案,因为a和b的gcd我们并不知道,So,再把a和b算下gcd验证一下,完美

那么这样做的时间复杂度是多少呢?
枚举a和c要一个n log n,验证算gcd又要一个log n,所以最终的复杂度—— O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
嗯,不错不错,相较于原来的 O ( n 2 l o g n ) O(n^2log n) O(n2logn)可以说是非常优啊!我们来愉快的算一下实际要多久——Oh,NOOOOO——187s,人品再怎么好,也卡不进那时限

诶,又要开始优化了:
首先,枚举a的那一维肯定省不了,那我们就来省掉一个logn,我们争取把检验做到O(1),我们试着把b用gcd算出来以后用异或去检验,So,How to do it?
我们知道了a 和 c 如果是答案的话那a和b一定是c的倍数,也就是a-c一定是c的倍数,因为b是c的倍数,也就是a xor c也是c的倍数,然后你要明白一个幼儿园小朋友都知道的事情—a-c ≤ a xor c ≤ a+c(如果你连幼儿园小朋友都不如,戳我),而b ≤ a,所以我们惊奇地发现a xor c只能取到a-c,也就是b=a xor c=a-c,哦!!b=a-c,我们用O(1)算出了b,但a xor b 的值我们不能确定,那我们在用O(1)放到异或里去检验一下,最终复杂度—— O ( n l o g n ) O(nlogn) O(nlogn)

好的,我们再来算一下实际用时,什么!!!!7.5s!!!,哎,最终还是到了拼rp的时候了,你只要坚定信念,你敢这么做,评测器就敢这么让你过(如果TLE了,说明你的人品是多差)

OK,完事


c++代码:

#include
using namespace std;

long long f[30000005];

int main(){
	int cas;
	for (int i=1;i<=15000000;i++){
		for (int j=2;j<=30000000/i;j++){
			 if (((i*j)^(i))==i*j-i) f[i*j]++;//我们用i*j枚举a,j枚举c
		}
	}
	for (int i=2;i<=30000000;i++){
		f[i]+=f[i-1];
	}
	scanf("%d",&cas);
	for (int i=1;i<=cas;i++){
		int n;
		scanf("%d",&n);
		printf("Case %d: %d\n",i,f[n]);
	}
	return 0;
}

于XJ机房607

你可能感兴趣的:(恶心的题目)