题目大意: 给出 n n n,求 ∑ 1 ≤ b ≤ a ≤ n [ gcd ( a , b ) = a x o r b ] \sum_{1\leq b \leq a \leq n}[\gcd(a,b)=a~xor~b] ∑1≤b≤a≤n[gcd(a,b)=a xor b]。
题解
有一个很重要的性质:当满足 gcd ( a , b ) = a x o r b \gcd(a,b)=a~xor~b gcd(a,b)=a xor b 时,有 gcd ( a , b ) = a x o r b = a − b \gcd(a,b)=a~xor~b=a-b gcd(a,b)=a xor b=a−b
证明
首先证明 a x o r b ≥ a − b a~xor~b\geq a-b a xor b≥a−b。
考虑将 a , b a,b a,b转成二进制。
分类讨论:
- 假如满足 a i = b i ( a i , b i 指 a 的 第 i 位 和 b 的 第 i 位 ) a_i=b_i(a_i,b_i指a的第i位和b的第i位) ai=bi(ai,bi指a的第i位和b的第i位),那么这一位显然满足 a x o r b = a − b a~xor~b=a-b a xor b=a−b。
- 假如满足
a
i
≠
b
i
a_i\not=b_i
ai=bi,那么有两种情况:
1、 a i = 1 , b i = 0 a_i=1,b_i=0 ai=1,bi=0,那么这种情况显然也满足 a x o r b = a − b a~xor~b=a-b a xor b=a−b。
2、 a i = 0 , b i = 1 a_i=0,b_i=1 ai=0,bi=1,那么这种情况满足 a x o r b > a − b a~xor~b>a-b a xor b>a−b。
综上, a x o r b ≥ a − b a~xor~b\geq a-b a xor b≥a−b。
得证。
然后证明 a x o r b = a − b a~xor~b=a-b a xor b=a−b。
设 c = gcd ( a , b ) = a x o r b c=\gcd(a,b)=a~xor~b c=gcd(a,b)=a xor b
∵
a
x
o
r
b
≥
a
−
b
\because a~xor~b\geq a-b
∵a xor b≥a−b
∴
a
−
b
≤
c
\therefore a-b\leq c
∴a−b≤c
∵
c
=
gcd
(
a
,
b
)
=
g
c
d
(
a
−
b
,
b
)
\because c=\gcd(a,b)=gcd(a-b,b)
∵c=gcd(a,b)=gcd(a−b,b)
∴
a
−
b
≥
c
\therefore a-b\geq c
∴a−b≥c
∴
a
−
b
=
c
\therefore a-b=c
∴a−b=c
∴
a
−
b
=
a
x
o
r
b
=
gcd
(
a
,
b
)
\therefore a-b=a~xor~b=\gcd(a,b)
∴a−b=a xor b=gcd(a,b)
得证
于是可以将 gcd ( a , b ) = a x o r b \gcd(a,b)=a~xor~b gcd(a,b)=a xor b 这个限制拆开成 gcd ( a , b ) = a − b ∧ a x o r b = a − b \gcd(a,b)=a-b~\land a~xor~b=a-b gcd(a,b)=a−b ∧a xor b=a−b这两个限制,因为 gcd ( a , b ) = a − b \gcd(a,b)=a-b gcd(a,b)=a−b移项得 b + g c d ( a , b ) = a b+gcd(a,b)=a b+gcd(a,b)=a, b b b一定是 gcd ( a , b ) \gcd(a,b) gcd(a,b)的倍数,于是就可以枚举 gcd ( a , b ) \gcd(a,b) gcd(a,b)和 b b b,然后判断是否满足 a x o r b = a − b a~xor~b=a-b a xor b=a−b即可。
可以证明该做法时间复杂度严格小于 O ( n l o g n ) O(nlogn) O(nlogn),然后题目也很良心给了 5 s 5s 5s,所以完美AC。
代码如下:
#include <cstdio>
#include <cstring>
#define maxn 30000010
#define ll long long
int t,n;
ll f[maxn];
int lowbit(int x){return x&(-x);}
int main()
{
for(int i=1;i<=maxn-10;i++)//枚举gcd
for(int j=2;j*i+i<=maxn-10;j++)//枚举b,(i*j)等于b,(i*j+i)等于a
if(((i*j)^i)==i*j+i)f[i*j+i]++;
for(int i=1;i<=maxn-10;i++)//统计前缀和,这样可以O(1)出解
f[i]+=f[i-1];
scanf("%d",&t);
int tot=0;
while(t--)
{
tot++;
scanf("%d",&n);
printf("Case %d: %lld\n",tot,f[n]);
}
}