您的位置:首页 > 其它

JZOJ4714公约数

2016-08-19 20:51 190 查看

题目描述

给定一个正整数,在[1,n]的范围内,求出有多少个无序数对(a,b)满足gcd(a,b)=a xor b。

对于30%的数据满足n<=1000

对于60%的数据满足n<=10^5

对于100%的数据满足n<=10^7

分析

30分:暴力枚举两个数直接判断;

有点难以下手,那么我们看到GCD可以想到先把GCD确定,再考虑。于是设GCD为c,那么我们现在要找的就是a xor b=c的方案数了。我们可以枚举a=i*c,b=j*c再判断gcd(a,b) 与 a xor b是否等于c。

这样似乎多此一举了,再看看如何优化?我们知道:若a xor b=c ,则a xor c=b,这样我们就可以不用枚举b了,因为b已经被a,c所确定,直接判断gcd就可以。那么时间复杂度降至O(Nlog2N)。

其实根本不必求gcd(a,b)。从二进制角度考虑xor:我们先把a,b,c看做二进制数,设a>b,可以发现:a最多只能被b改变b这么多,就是说b的所有位都跟a对应的一样。

那么gcd(a,b)≤a-b,a xor b≥a-b

明显:a-b=c。

整理一下:有三个条件,

1,gcd(a,b)=c

2, a xor b=c

3, a-b=c

其中满足1,2,3就随之满足。

我们枚举的a是c的倍数,那么b满足3就能满足1:

gcd(i*c,i*c+1)=gcd(i,i+1)*c=1*c。

那么我们剩下的只是2了。

所以我们枚举c和a,则b=a-c。判断条件2即可。

nlogn解决。常数很小可以过。

代码

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
long long ans;
int n,x,i,j,l,r,le,ri;
int main()
{
ans=0;
scanf("%d",&n);
fo(i,1,n)
{
fo(j,1,n/i-1)
{
if (((i*j)^(i*j+i))==i)
ans++;
}
}
printf("%lld\n",ans);
}


反思

比赛的时候这道题想了很久,写博客也是断断续续的,究其原因是逻辑不清,数学思维不清晰。数学题如果没有太了解相关的性质,就需要去做猜想,证明然后理清楚性质,与各种约束关系。最后寻找可行方法。

比如这题我很快能推出3个条件,但是不能把它们表达清楚更理不好关系,不存在清晰逻辑,而且对gcd性质不够熟悉,导致想了很久很久,才试了出来。

其实还有些隐藏属性,比赛的时候时间不够不管了。

所以对题目的条件或满足的性质,要清晰地以式子表达,理清关系,这样想算法才会快。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: