经过长时间思考并解决调问题的感觉太好了 ——xls
网上的题解比较少而且都讲的比较跳跃,不知道是他们太聪明还是我太笨了。于是本着刨根问底的精神我详细推导了下过程。如果想麻烦了欢迎指正。
首先,farey 数列的分母构成的数列一定是对称的,因为如果分子与分母互质,那么分母与分子的差也一定与分母互质,这个可以用反证法证明:设分母是 ,分子是 ,如果 与 不互质,那么可以写成 那么 与 也不互质,所以 与 要么都在数列里要么都不在数列里。
其次,设当前的 order 是 ,那么当 order 增加到 时,将会有 个数被插入,这个道理很简单:如果不是互质的话就被约掉了。
下面我们看一下插入的这些数对 farey sums 有什么影响:
设 插到了 与 之中,我看到的题解都直接给出了结论 这个结论看起来很神奇(事实上还有 ),但我怎么也想不出来这个是怎么得到的,于是我上了维基百科得到了思路:
首先要先证明 与 如果在 order 为 中是相邻的两项(假设 在后,写完才发现后面证明把两个弄反了,懒的改了……)那么有 即 ,这个维基上也没给出证明,不过比较好想,依然是反证法:如果两个数之间还有其他的数 ,那么 ,如果 我们就看前面那个不等式 ,通分得 ,因为 所以 ,但因为 order 为 所以 不能大于 ,与假设矛盾。 的情况与前面同理。
有了这个我们就可以轻松证明当 与 之间有新的数 插入时那么有 移项得 ,最终得到
明白了这关键的一步之后,原来 farey sums 中和 (数列中对称的两项)就变成了 ,所以每插入两项,farey sums 就增加 3,一共插入了 项,那么 farey sums 就增加了 ,又因为 order 从 0 变成 1 的时候只增加了 1,比 少了,所以最终答案应为
代码
#include <iostream>
#define forn(i, n) for (int i = 0; i < (int)(n); ++i)
#define for1(i, n) for (int i = 1; i <= (int)(n); ++i)
using namespace std;
const int N = 10005;
int phi[N], phisum[N];
void phi_table(int n) {
phi[1] = 1;
for (int i = 2; i <= n; i++)
if (!phi[i])
for (int j = i; j <= n; j += i) {
if (!phi[j])
phi[j] = j;
phi[j] = phi[j] / i * (i - 1);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
phi_table(10000);
for1(i, 10000) phisum[i] = phisum[i - 1] + phi[i];
for1(i, n) {
int p, x;
cin >> x >> p;
cout << x << ' ' << (3 * phisum[p] - 1) << "/2\n";
}
return 0;
}