bzoj3025 [Balkan2003]Farey数列

我们考虑在 \(\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);
}
posted @ 2021-12-13 17:21  soroboruo  阅读(66)  评论(0编辑  收藏  举报