来源
目标
输入正整数 N ( 1 ≤ N ≤ 1 0 10 ) N(1\leq N\leq 10^{10}) N(1≤N≤1010),找出所有满足 1 ≤ M ≤ N 1\leq M\leq N 1≤M≤N以及 gcd ( N , M ) = N ⊕ M (1) \gcd(N,M)=N\oplus M\tag{1} gcd(N,M)=N⊕M(1)的整数 M M M1. 程序运行的时间限制1s.
初步复杂度分析
由于 N N N最大可以到 1 0 10 10^{10} 1010,且原题是要求多组数据输入,不难看出 O ( N ) O(N) O(N)时间复杂度的算法很容易超时. 因此,直接按照题目意思枚举是不可行的. 由于涉及到 gcd \gcd gcd函数,可以想象,如果能够将搜索范围缩小为 N N N的约数,那么就很有可能将复杂度降至 O ( N ) O(\sqrt{N}) O(N),进而只需要大约 1 0 5 10^5 105级别的运算量.
异或的性质
此处就需要利用异或的一个性质:若
X
⊕
Y
=
Z
X\oplus Y=Z
X⊕Y=Z,则
X
⊕
Z
=
Y
X\oplus Z=Y
X⊕Z=Y. 由这个性质,
(
1
)
(1)
(1)式可以被改写为
N
⊕
gcd
(
N
,
M
)
=
M
.
(2)
N\oplus\gcd(N,M)=M\tag{2}.
N⊕gcd(N,M)=M.(2)
而
gcd
(
N
,
M
)
∣
N
\gcd(N,M)\mid N
gcd(N,M)∣N,因此相比于枚举
M
M
M然后求解
gcd
(
N
,
M
)
\gcd(N,M)
gcd(N,M)这样一个
O
(
N
)
O(N)
O(N)的思路,反过来枚举所有
N
N
N的约数
a
a
a,然后利用
M
=
N
⊕
a
M=N\oplus a
M=N⊕a计算出
M
M
M的值,再检查
a
a
a是否等于
gcd
(
N
,
M
)
\gcd(N,M)
gcd(N,M)效率更高. 如果检查通过,则找到了一个解. 该方法最多只需枚举
N
N
N的所有约数,这是
O
(
N
)
O(\sqrt{N})
O(N)就可以实现的. 这样,该问题就得到了解决. (代码见后文)
这个问题其实到这里就结束了. 不过,还有一条与异或、最大公约数有关的性质也值得在这里展示:
gcd
(
a
,
b
)
≤
∣
a
−
b
∣
≤
a
⊕
b
.
(3)
\gcd(a,b)\leq |a-b|\leq a\oplus b\tag{3}.
gcd(a,b)≤∣a−b∣≤a⊕b.(3)
这个性质适用于所有以其正常二进制编码进行按位异或运算的正整数
a
,
b
a,b
a,b.
不等式的前半部分的说明用
gcd
\gcd
gcd函数的基本性质即可实现:
不妨设
a
>
b
a>b
a>b. 设
c
=
gcd
(
a
,
b
)
c=\gcd(a,b)
c=gcd(a,b),则
a
=
m
c
,
b
=
n
c
(
m
,
n
∈
N
∗
)
a=mc,b=nc(m,n\in\mathbf{N^*})
a=mc,b=nc(m,n∈N∗),
∣
a
−
b
∣
=
a
−
b
=
(
m
−
n
)
c
|a-b|=a-b=(m-n)c
∣a−b∣=a−b=(m−n)c. 此处
m
−
n
m-n
m−n必然大于等于
1
1
1,因此
gcd
(
a
,
b
)
≤
∣
a
−
b
∣
\gcd(a,b)\leq|a-b|
gcd(a,b)≤∣a−b∣.
而后半部分则主要是基于按位异或运算可以等效于“无借位减法”的认识(其实同时也是“无进位加法”),此处就不给出数学意义上的严格证明。以下的真值表可以说明这一点:
X X X | Y Y Y | X ⊕ Y X\oplus Y X⊕Y | difference ( X , Y ) \text{difference}(X,Y) difference(X,Y) | borrow ( X , Y ) \text{borrow}(X,Y) borrow(X,Y) |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 | 1 |
1 | 0 | 1 | 1 | 0 |
1 | 1 | 0 | 0 | 0 |
UVa12716是一道与本题类似的问题,它的解法往往需要利用 ( 3 ) (3) (3)中提到的性质.
代码
#include <iostream>
#include <cmath>
#include <set>
long long gcd(long long a, long long b)
{
return b == 0 ? a : gcd(b, a % b);
}
inline bool check(long long n, long long i)
{
long long xor_res = n ^ i;
if (xor_res >= 1 && xor_res <= n
&& xor_res % i == 0 && gcd(xor_res, n) == i)
{
return true;
}
return false;
}
int main()
{
using std::cin;
cin.sync_with_stdio(false);
cin.tie(nullptr);
long long n;
while (cin >> n)
{
std::set<long long> sst;
int cnt = 0;
for (int i = 1; i <= std::sqrt(n); i++)
{
if (n % i != 0)
{
continue;
}
if (check(n, i))
{
sst.insert(n ^ i);
}
if (n / i != i && check(n, n / i))
{
sst.insert(n ^ (n / i));
}
}
std::cout << sst.size() << std::endl;
if (sst.size())
{
auto it = sst.cbegin();
std::cout << *it;
it++;
for (; it != sst.cend();
it++)
{
std::cout << " " << *it;
}
}
std::cout << std::endl;
}
return 0;
}
写在最后
本文提供的解题思路及程序代码仅供参考,并不保证能够通过特定Judger的评测,更不保证它们得到了充分的优化.
gcd ( m , n ) \gcd(m,n) gcd(m,n)为 m m m和 n n n的最大公约数,“ ⊕ \oplus ⊕”为按位异或(bitwise XOR). ↩︎