题目链接:这里~~
题目描述:输入数据组数t,接下来t行每行给定一个数字n,如样例所示格式输出满足1<=b<=a<=n且gcd(a,b)==a xor b的(a,b)二元组个数。
2 7 20000000
输出样例:
Case 1: 4 Case 2: 34866117
解题思路:
首先需要知道三个性质:
1.A^B=C , 则 A^C=B , B^C=A.
2.(A^B)^C=A^(B^C)
3.若A>=B, 则A-B<=A^B
然后观察题目条件:
等式1:GCD(A,B)=A^B=GCD(B,A-B)= C (假设一个C)
设A>=B , A=k1*C , B=k2*C. 则 A-B=(K1-K2)*C
当A=B ,等式1显然不成立,除非A=0.所以K1!=K2
因为K1-K2>=1,所以A-B>=C, 又因为上面的性质为A-B<=A^B=C.
即A-B=C
则GCD(B,C)=C.
我们只需要枚举C的倍数(B)就行了。
满足了GCD(A,B)=C的限制条件,发现我们还没有用A^B=C的限制条件.
所以我们在枚举B的时候,对于满足(B+C)^B=C的更新答案即可。
当然A不能大于N,所以B+C<=N。
因为T可能很多,可以发现实际上答案是可以预处理的且具有前缀和的性质。
时间复杂度:O(Nlog(N)).
分析:
对于N是枚举C.
log(n)是枚举C的倍数,每个数的倍数在N的范围内不超过N/C个,
对于所有的数则是(N/1+N/2+N/3+...+N/N),时间复杂度为调和级数,
所以总时间复杂度为N log(N).
代码:
#include<bits/stdc++.h> #define ll long long #define R register using namespace std; const int N=3e7+5; int t,n; ll ans[N]; inline void find() { for(R int i=1;i<=30000000;++i)//枚举c for(R int j=i*2;(i+j)<=30000000;j+=i) if((i+j)==(i^j))++ans[i+j]; for(R int i=1;i<=30000000;++i) ans[i]+=ans[i-1]; } int main(){ scanf("%d",&t); find(); for(R int k=1;k<=t;k++) { scanf("%d",&n); printf("Case %d: %lld ",k,ans[n]); } return 0; }