2298. 异或
题目描述
SarvaTathagata这么神仙的人当然觉得这个是sb题啦。学习之余,他还发现gcd的某一个特别好的性质:如果有两个数i,j满足gcd(i,j)=i^j(这里的^为c++中的异或)的话,那么这两个数组成的数对(i,j)就是一个nb的数对(这里认为(i,j)和(j,i)为相同的,并不需要计算2次)。
当然,SarvaTathagata并不会只满足于判断一个数对是否nb,他还想知道满足两个数都是不超过n并且nb的数对有多少个。
由于SarvaTathagata实在是太神仙了,他认为这种题实在是太简单了。于是他找到了你,看看你是否能解决这个问题。
输入
输出
样例输入
样例输入1 12 样例输入2 123456
样例输出
样例输出1 8 样例输出2 214394
数据范围限制
![](https://i-blog.csdnimg.cn/blog_migrate/0fb3a9044556263f9519d3500064db7c.png)
提示
题解:
首先我们看到这一道题,聪明的你一定发现了第10个点:n<=20000000。
但是,先不要慌,我们先来证一个不等式:
证明:x ^ y >= x - y >= gcd( x , y )。(其中‘^’代表异或)
我们先来证明左边。
从上面那一张异或与减运算的竖式中可以看出:
其实"^"和"-"运算唯一不同的地方仅仅在于运算方法上的不同。
所以我们不妨将上图中所有的数都转化成二进制。
于是就有:
这时候我们注意一下下图中圈了红圈的地方:
我们可以发现:当a=0,b=1时,a异或b=1,而a-b=-1(图中具体表现为退位)。
为了让你更好的理解,下面请看一张分析表:
所以,我们可知,对于两个数的对应的每一位a,b,均有:
a xor b >= a - b 。(xor表示异或)
所以 x xor y >= x - y ,当且仅当x与y的对应二进制位不为0和1时,等号成立。
原式左边得证。
下面我们再看到右边:x - y >= gcd( x , y )。
根据更相减损术:gcd( x , y )=gcd( y , x-y ),( x > y )
所以原式等价于x - y >=gcd( y , x-y )。
又因为b = gcd( a , b ) * k (k >= 1),
所以x - y >=gcd( y , x-y )这个式子显然成立。
原式右边得证。
所以原式得证。
现在我们回到原题,题目的条件为:
i xor j = gcd( i , j )。
我们将它带入那个不等式中,可得:
i xor j = i - j = gcd( i , j )。
也就是说,i - j = i xor j。
所以我们只要枚举i与i xor j就行了。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,sum; 4 int main() 5 { 6 cin>>n; 7 for(int c=1;c<=n;c++)//枚举a-b(同时也是gcd(a,b)) 8 for(int a=2*c;a<=n;a+=c)//因为a>=b,所以从a=2*c开始枚举 9 if(c==(a ^ a-c))sum++;//如果 c=a^b(b=a-c),答案++ 10 printf("%d",sum);//输出答案 11 return 0; 12 }
可是,n<=20000000啊!
没事,不慌。
相信我。
你们知道微积分吗?
如果不知道,我现场告诉你一个公式:
n/1+n/2+n/3+……+n/n=n*logen (因为本人是初中蒟蒻,所以只知道有这东西)
你知道这意味着什么吗?
我们的程序的时间复杂度其实是n*logen=n*logn !!!
所以……可以AC!
(本人的温馨提示:建议大家最好像上面的代码一样,不然会被卡常!)