UVA.12716 GCD XOR (暴力枚举 数论GCD)

UVA.12716 GCD XOR (暴力枚举 数论GCD)

题意分析

题意比较简单,求[1,n]范围内的整数队a,b(a<=b)的个数,使得 gcd(a,b) = a XOR b.

前置技能

XOR的性质
GCD

由于题目只给出一个n,我们要求对数,能做的也始终暴力枚举a,b,这样就有n^2的复杂度,由于n很大,根本过不了。

于是我们就想用到其中一些性质,如XOR 与GCD,不妨假设 a xor b = c,并且根据题意还知道, gcd(a,b) = c,也就说明c一定是a的因子,所以在枚举的时候,可以转过头来枚举a和c.那么如何求出当前情况下的b呢,考虑到xor的性质,即 a xor b = c, a xor c = a xor a xor b = b. 通过异或运算就可以求解出来b,然后再检验gcd(a,b)是否为c即可。

到这里其实已经足够了,但是打出一定规模符合题意的(a,b,c),不难发现,a-b=c,有了这条性质,就可以不用gcd检验了。换句话说,通过枚举a,c,b = a-c计算出b,通过a^b=c检验是否符合条件。因为相对而言,位运算比gcd快得多。

值得一提的是,由于n很大,连续处理多个n很大的值的时候,速度表现不能令人满意,最先想到的办法就是打表方法。

代码总览

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define nmax 30000010
#define ll long long
using namespace std;
int n;
int t;
int num[nmax];

void init(){
    for(int c = 1;c<=(nmax+1)/2;++c){
        for(int a = c+c;a<nmax;a+=c){
            int b = a-c;
            if((a^b) == c) num[a]++;
        }
    }
    for(int i = 2;i<nmax;++i)  num[i]+=num[i-1];
}

int main()
{
    int kase =1 ;
    init();
    scanf("%d",&t);
    for(kase  = 1; kase <=t;++kase){
        scanf("%d",&n);
        printf("Case %d: %d\n",kase,num[n]);
    }
    return 0;
}
posted @ 2017-08-10 11:33  pengwill  阅读(344)  评论(0编辑  收藏  举报