「TFOI R1」Average Number 题解

· · 题解

出题人题解。

原本预估的难度是黄,但是从反馈来看的话难度是绿(

题意应该很清楚,我的解法是推式子+缩放,一些奇怪的方法可以看 Super_Cube 的题解。

\begin{aligned} &\dfrac{\dfrac{n \times (n + 1)}{2} - m}{n - 1}\\ =&\dfrac{n \times (n + 1) - 2 \times m}{2 \times (n - 1)}\\ =&\dfrac{n \times (n - 1) + 2 \times n - 2 \times m}{2 \times (n - 1)}\\ =&\dfrac{n}{2} + \dfrac{n - m}{n - 1}\\ =&a + \dfrac{b}{c}\\ \end{aligned}

接下来是缩放,也是我认为的这一道题最精彩的部分。

注意到 1 \leqslant m \leqslant n,所以 0 \leqslant n - m \leqslant n - 1

所以 0 \leqslant \dfrac{n - m}{n - 1} \leqslant 1

带入到上面的式子后就是:

\dfrac{n}{2} \leqslant \dfrac{n}{2} + \dfrac{n - m}{n - 1} = a + \dfrac{b}{c} \leqslant \dfrac{n}{2} + 1

因为 \dfrac{b}{c} 是最简真分数(也就是小于 1),所以又有:

\dfrac{n}{2} - 1 < a \leqslant \dfrac{n}{2} + 1

把它拆成两个不等式并化简:

\begin{cases} n < 2\times a + 2\\ 2 \times a - 2 \leqslant n \end{cases}

(因为 n 是正整数,所以 n < 2\times a + 2 等价于 n \leqslant 2\times a + 1

最后就得到了:2 \times a - 2 \leqslant n \leqslant 2 \times a + 1

于是可以在这个范围枚举 n,也就带一个四倍的常数。

$$ \begin{aligned} \dfrac{\dfrac{n \times (n + 1)}{2} - m}{n - 1}&= \dfrac{a \times c + b}{c}\\ \dfrac{n \times (n + 1)}{2} - m&= \dfrac{(n - 1) \times (a \times c + b)}{c}\\ m&= \dfrac{n \times (n + 1)}{2} - \dfrac{(n - 1) \times (a \times c + b)}{c}\\ \end{aligned} $$ 因为 $m$ 也是正整数,所以要检查一下 $c$ 能不能整除 $n - 1$。 要注意上面的式子算出来的 $m$ 仍然可能不合法(比如出现负数),以及 $a = 1$ 时 $2 \times a - 2 = 0$ 但是 $n \geqslant 2$ 要判断一下。 运算过程中变量可能超 `long long`,记得开 `__int128` 还有优化运算顺序。 最后给出代码: ```cpp #include <bits/stdc++.h> using namespace std; #define getchar gc typedef __int128 ll; #define fu(i, l, r) for(ll i = l; i <= r; ++i) char gc() {static char ibuf[1 << 20], *p1 = ibuf, *p2 = ibuf; return p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1000010, stdin), p1 == p2) ? -1 : *p1++;} void read(ll& x) {x = 0; int f = 1; char ch = getchar(); while(ch < '0' || ch > '9') f *= ((ch == '-') ? -1 : 1), ch = getchar(); while(ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar(); x *= f;} ll t, m, a, b, c; int main() { read(t); while(t--) { read(a), read(b), read(c); fu(i, max(2 * a - 2, (ll)2), 2 * a + 1) { if((i - 1) % c) continue; m = i * (i + 1) / 2 - (i - 1) / c * (a * c + b); if(m < 1 || m > i) continue; printf("%lld %lld\n", (long long)i, (long long)m); break; } } return 0; } ```