题目
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=49096
题解
这道题开始完全懵逼,只知道n^2乱搞。(看着xor我表示有心理压力,SCOI2016后遗症,而且我不会告诉你我开始是奔着把所有的数对求出来的心理做的。)然后手玩数据,发现(2k,2k+1)肯定是一组,这就有floor((n-1)/2)这么多的组数了,然后打表去看gcd(a,b)==a xor b=k(2<=k<=100)的数对,然而并没有什么卵用。我甚至忽略了a-b==gcd(a,b)这个重要的东西。
正题:xor有个神奇的性质
if a xor b==c __ ==>a xor c==b
又因为c是a的约数,所以枚举a,c的复杂度一共是nlogn
插一句,为什么是nlogn?本蒟蒻想的是枚举a然后sqrt(a)枚举约数,然而实际上是先枚举c,再for(int a=c*2;a<=30000000;a+=c) ,(我智商确实低),所以这就和筛素数一样了,nlogn(其实c只到30000000/2即可,常数小)
a,c知道了,则b=a xor c,若gcd(a,b)==c,则成立。gcd的复杂度。。我只知道很小,刘汝佳说是logn(感觉也够小了),那么整个时间复杂度就是O(n(logn)^2).(讲道理我觉得也许能卡着过?)
拓展:打表之后我发现gcd(a,b)==a-b;所以c==a-b,所以b=a-c;这时gcd(a,b)一定为c,简易证明:
设a=a’ * c;
则b=a+c=(a’ +1)*c;
因为gcd(a’,a’+1)==1;
所以gcd(a,b)==c;
所以gcd那个log就去掉了,而且完全不用写gcd(虽然也就一两行)。
我觉得我要是没有想到像筛法一样的枚举顺序,想到了所有的点也做不出来。智商感人。
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=30000000+10;
int ans[maxn],cnt[maxn];
inline int read()
{
int ret=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0';
return ret;
}
int main()
{
int n=30000000;
for(int c=1;c<=n/2;c++)
for(int a=c<<1;a<=n;a+=c)
{
int b=a-c;
if(c==(a xor b)) cnt[a]++;
}
for(int i=1;i<=n;i++) ans[i]=ans[i-1]+cnt[i];
int T,k,kase=0;cin>>T;
while(T--)
{
k=read();
printf("Case %d: %d\n",++kase,ans[k]);
}
return 0;
}