Codeforces 871D Paths (欧拉函数 + 结论)
题目链接 Round #440 Div 1 Problem D
题意 把每个数看成一个点,如果$gcd(x, y) \neq 1$,则在$x$和$y$之间连一条长度为$1$的无向边。
设$d(u, v)$为$u$到$v$之间的最短路,如果$u$和v不连通那么$d(u, v) = 0$
现在给定$n$,求所有的满足$1 <= u < v <= n$的$d(u, v)$之和。
首先把$1$和大于$\frac{n}{2}$的质数去掉,这些数和任何数之间的最短距离为$0$。
我们可以得出对于任意$u$, $v$,都有$d(u, v) <= 3$
若$u$和$v$非互素,那么$d(u, v) = 1$;
令$p(x)$为$x$的最小质因子。如果$p(u) \cdot p(v) <= n$,那么$d(u, v) = 2$
路径为$u - p(u) \cdot p(v) - v$
否则一定存在一条长度为3的路径:$u - 2u - 2v - v$
那么只要求出这三种路径的条数就可以了。
对于长度为$1$的路径,利用欧拉函数可以轻松求出。
对于长度为$2$的路径,设$c[x]$为$p[u] = x$的$u$的个数,$s[]$为$c[]$的前缀和。
那么长度为$2$的路径条数为$∑c_{i} * s_{[\frac{n}{i}]}$,注意去掉长度为$1$的情况。
最后长度为$3$的路径条数就是总的合法点对数减去长度为$1$的路径和长度为$2$的路径条数。
时间复杂度$O(nlogn)$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define MP make_pair #define fi first #define se second typedef long long LL; const int N = 1e7 + 10; int pri[N], p[N], phi[N], c[N], s[N]; int n, m, tot, now; LL s1, s2, s3; int main(){ scanf("%d", &n); phi[1] = 1; rep(i, 2, n){ if (!p[i]){ p[i] = pri[++tot] = i; phi[i] = i - 1; } rep(j, 1, tot){ if (i * pri[j] > n) break; p[i * pri[j]] = pri[j]; if (i % pri[j] == 0){ phi[i * pri[j]] = phi[i] * pri[j]; break; } else phi[i * pri[j]] = phi[i] * (pri[j] - 1); } } rep(i, 2, n) s1 += 0ll + i - 1 - phi[i]; rep(i, 2, n) ++c[p[i]]; rep(i, 2, n) s[i] = s[i - 1] + c[i]; rep(i, 2, n) s2 += 1ll * c[i] * s[n / i]; rep(i, 2, n) if (1ll * p[i] * p[i] <= n) --s2; s2 /= 2; s2 -= s1; m = n - 1; dec(i, tot, 1){ if (pri[i] * 2 > n) --m; else break; } s3 = 1ll * m * (m - 1) / 2 - s1 - s2; printf("%lld\n", s1 + 2 * s2 + 3 * s3); return 0; }