D2. Reverse Card (Hard Version)
题目描述
给你两个正整数 n n n , m m m 。
请计算满足以下条件的有序数对 ( a , b ) (a, b) (a,b) 的个数:
- 1 ≤ a ≤ n 1\le a\le n 1≤a≤n , 1 ≤ b ≤ m 1\le b\le m 1≤b≤m ;
- b ⋅ gcd ( a , b ) b \cdot \gcd(a,b) b⋅gcd(a,b) 是 a + b a+b a+b 的倍数。
输入描述
每个测试包含多个测试用例。第一行包含测试用例的数量 t t t ( 1 ≤ t ≤ 1 0 4 1\le t\le 10^4 1≤t≤104 )。测试用例说明如下。
每个测试用例的第一行包含两个整数 n n n , m m m ( 1 ≤ n , m ≤ 2 ⋅ 1 0 6 1\le n,m\le 2 \cdot 10^6 1≤n,m≤2⋅106 )。
保证所有测试用例中 n n n 和 m m m 的总和不超过 2 ⋅ 1 0 6 2 \cdot 10^6 2⋅106 。
输出描述
为每个测试用例打印一个整数:有效配对的数量。
样例输入
6
1 1
2 3
3 5
10 8
100 1233
1000000 1145141
样例输出
0
1
1
6
423
5933961
原题
思路
CF题解——传送门
令
gcd
(
a
,
b
)
=
d
\gcd(a,b)=d
gcd(a,b)=d ,则
a
=
p
d
a=pd
a=pd 和
b
=
q
d
b=qd
b=qd ,所以
gcd
(
p
,
q
)
=
1
\gcd(p,q)=1
gcd(p,q)=1 。于是条件可等价推导:
(
a
+
b
)
∣
(
b
⋅
gcd
(
a
,
b
)
)
⟺
(
p
d
+
q
d
)
∣
(
q
d
2
)
⟺
(
p
+
q
)
∣
(
q
d
)
(a+b)\mid (b\cdot\gcd(a,b))\iff (pd+qd)\mid (qd^2)\iff (p+q)\mid (qd)
(a+b)∣(b⋅gcd(a,b))⟺(pd+qd)∣(qd2)⟺(p+q)∣(qd) 。又因为
gcd
(
p
+
q
,
q
)
=
gcd
(
p
,
q
)
=
1
\gcd(p+q,q)=\gcd(p,q)=1
gcd(p+q,q)=gcd(p,q)=1 ,所以
(
p
+
q
)
∤
q
(p+q) \nmid q
(p+q)∤q,所以
(
p
+
q
)
∣
d
(p+q)\mid d
(p+q)∣d 。此外,因为
p
≥
1
,
q
≥
1
p\ge 1,q\ge 1
p≥1,q≥1 ,
p
<
d
=
a
p
≤
n
p
p < d=\frac{a}{p}\le \frac{n}{p}
p<d=pa≤pn ,所以
p
2
<
n
p^2 < n
p2<n 。同理,
q
2
<
m
q^2 < m
q2<m 。所以
(
p
,
q
)
(p,q)
(p,q) 的个数是
O
(
n
m
)
=
O
(
n
+
m
)
\mathcal O(\sqrt{nm})=\mathcal O(n+m)
O(nm)=O(n+m) 。所以可以枚举
gcd
(
p
,
q
)
=
1
\gcd(p,q)=1
gcd(p,q)=1 的每个
(
p
,
q
)
(p,q)
(p,q) ,找出其中满足
(
p
+
q
)
∣
d
(p+q)\mid d
(p+q)∣d 的序对,即加上
⌊
min
{
⌊
n
p
⌋
,
⌊
m
q
⌋
}
p
+
q
⌋
\left\lfloor\frac{\min\{\lfloor\frac{n}{p}\rfloor,\lfloor\frac{m}{q}\rfloor\}}{p+q}\right\rfloor
⌊p+qmin{⌊pn⌋,⌊qm⌋}⌋ 。
PS:这场Div.2的C题被Hack了,由于二分查找上界写小了。所以我很难过。 所以以后要注意仔细想一下边界,不要想当然了()。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
ll n, m;
cin >> n >> m;
ll maxp = sqrt(n) + 6;
ll maxq = sqrt(m) + 6;
vector<vector<bool>> not_gcd_equal_to_1(maxp + 1, vector<bool>(maxq + 1, 0));
// 预处理所有gcd(p,q)≠1的序对
for (ll k = 2; k <= min(maxp, maxq); k++)
{
for (ll i = k; i <= maxp; i += k)
{
for (ll j = k; j <= maxq; j += k)
{
not_gcd_equal_to_1[i][j] = 1;
}
}
}
ll ans = 0;
for (ll p = 1; p * p <= n; p++)
{
for (ll q = 1; q * q <= m; q++)
{
if (!not_gcd_equal_to_1[p][q])
{
ans += min(n / p / (p + q), m / q / (p + q));
}
}
}
cout << ans << '\n';
}
return 0;
}