我们考虑在 \(\text{Stern-brocot tree}\) 上面二分,每次判断有多少个既约分数不超过当前枚举的节点。
\[\sum\limits_{i=1}^n\sum\limits_{j=1}^n[\gcd(i,j)==1][Bj\le Ai]\\
=\sum\limits_{d=1}^n\mu(d)\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}\sum\limits_{j=1}^{\lfloor\frac nd\rfloor}[Bj\le Ai]\\
=\sum\limits_{d=1}^n\mu(d)F(A,0,B,\lfloor\frac nd\rfloor)
\]
这个 \(F\) 的计算是类欧几里得,因为太久没写于是重新推一下,先假装有 \(a,b<c\),否则可以模掉:
\[\begin{aligned}
F(a,b,c,n)=&\sum\limits_{i=0}^n\lfloor\frac{ai+b}{c}\rfloor\\
令m_i=&\lfloor\frac{ai+b}c\rfloor\\
=&\sum\limits_{i=0}^n\sum\limits_{j=0}^{m_i-1}1\\
=&\sum\limits_{j=0}^{m_n-1}\sum\limits_{i=0}^n[ai+b\ge c(j+1)]\\
=&\sum\limits_{j=0}^{m_n-1}(n-\sum\limits_{i=0}^n[ai+b<c(j+1)])\\
=&m_n*n-F(c,c-b-1,a,m_n-1)
\end{aligned}
\]
其实这样访问到的深度可能很深,如果是多次询问需要二分拐点。
code
inline ll F(ll a, ll b, ll c, ll n) {
if (a >= c || b >= c) return n * (n + 1) / 2 * (a / c) + (n + 1) * (b / c) + F(a % c, b % c, c, n);
ll m = (a * n + b) / c;
if (!m) return 0;
return m * n - F(c, c - b - 1, a, m - 1);
}
inline ll calc(ll A, ll B) {
ll res = 0;
for (int t, l = 1, r; l <= n; l = r + 1) {
r = n / (t = n / l);
res += (smu[r] - smu[l - 1]) * F(A, 0, B, t);
}
return res;
}
inline void solve(int x1, int y1, int x2, int y2) {
int x = x1 + x2, y = y1 + y2;
ll cmp = calc(x, y);
if (cmp == K) {
cout << x << ' ' << y;
exit(0);
}
if (cmp > K) solve(x1, y1, x, y);
else solve(x, y, x2, y2);
}