CF870F
CF870F
题意
给定一张 \(n\) 个顶点的图,对于点 \(i,j\),如果 \(\gcd(i,j)\neq 1\),则 \(i\) 到 \(j\) 有一条长度为 \(1\) 的无向边。
令 \(dis(i,j)\) 表示从 \(i\) 到 \(j\) 的最短路,如果 \(i\) 无法到 \(j\),则 \(dis(i,j)=0\)。求节点两两之间距离之和。
Tips: \(n \leq 10^7\)
题解
一个分类讨论直接秒了。
方便起见,我们记 \(p_i\) 为 \(i\) 的最小质因子, \(num_i\) 为 \(i\) 是最小质因子出现的个数。
Case A
两两不互质
那么可以直接到达,距离为1。
贡献 \(1 \times \sum_{i=1}^{n}\sum_{j=i+1}^{n}[\gcd(i,j) \neq 1]\)
枚举 \(i\), 然后欧拉函数随便做。
答案是 \(\binom{n-1}{2} - \sum_{i=1}^{n}(\varphi_i - 1)\)
Case B
两两互质,且 \(p_ip_j \leq n\)
以 \(p_ip_j\) 作为中转站,距离为2。
贡献 \(2 \times \sum_{i=1}^{n}\sum_{j=i+1}^{n}[\gcd(i, j)=1][p_ip_j \leq n]\)
统计的话,总方案数减去 Case A、C、D 的就行了。
Case C
两两互质,且 \(p_ip_j > n(p_i \leq \frac{n}{2}\) 且 \(p_j \leq \frac{n}{2})\)
那就先到 \(2p_i\) 再到 \(2p_j\) 最后到 \(j\), 距离为3。
贡献 \(3 \times \sum_{i=1}^{n}\sum_{j=i+1}^{n}[p_i\leq \frac{n}{2}][p_j \leq \frac{n}{2}][p_ip_j > n]\)
化简一下,枚举最小质因子。
\(\sum_{p \in \mathbb{P}}num_p\sum_{q \in \mathbb{P}, p<q, q \leq \frac{n}{2}, pq \leq n} num_q\)
然后我们枚举 \(p\), q可以发现是连续的一段,前缀和即可,区间是 \([\max({p+1,\dfrac{n}{p}+1}),\dfrac{n}{2}]\)
Case D
两两互质,且\(p_ip_j > n(p_i > \dfrac{n}{2}\) 且 \(p_j > \dfrac{n}{2})\)
那就到达不了,但要统计一下方案数。
贡献 \(0 \times \sum_{i=1}^{n}\sum_{j=i+1}^{n}[\gcd(i,j)=1][p_i > \dfrac{n}{2}][p_j > \dfrac{n}{2}][p_ip_j > n]\)
跟 Case C 同理化简,然后前缀和,区间查就行了。
Code
Talk is useless, we see the code.
#include <bits/stdc++.h>
using ll = long long;
static const int N = 1e7 + 5;
int n, cnt; ll ans, ans0, ans1, ans2, ans3;
int a[N], c[N], p[N], prime[N];
ll phi[N]; bool vis[N];
void sieve(int n) {
phi[1] = 1; vis[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!vis[i]) prime[++ cnt] = i, p[i] = i, phi[i] = i - 1;
for (int j = 1; 1ll * i * prime[j] <= n; ++j) {
vis[i * prime[j]] = true;
p[i * prime[j]] = prime[j];
phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
if (i % prime[j] == 0) break;
}
}
}
ll ask(int l, int r) {return l > r ? 0 : c[r] - c[l - 1];}
signed main() {
scanf("%d", &n); sieve(n);
for (int i = 1; i <= n; ++i) phi[i] += phi[i - 1];
for (int i = 2; i <= n; ++i) ++ a[p[i]];
for (int i = 1; i <= n; ++i) c[i] += c[i - 1] + a[i];
ans1 = 1ll * (n - 1) * (n - 2) / 2 + n - phi[n];
for (int i = 1; i <= cnt; ++i) {
int x = prime[i];
int l = std::max(x, n / x) + 1, r = n / 2;
ans3 += ask(l, r) * a[x];
}
for (int i = 1; i <= cnt; ++i) {
int x = prime[i];
int l = std::max(x, n / 2) + 1, r = n;
ans0 += ask(l, r) * a[x];
}
ans2 = 1ll * (n - 1) * (n - 2) / 2 - ans0 - ans1 - ans3;
ans = 0 * ans0 + 1 * ans1 + 2 * ans2 + 3 * ans3;
return printf("%lld\n", ans), 0;
}
本文来自博客园,作者:xxcxu,转载请注明原文链接:https://www.cnblogs.com/Maraschino/p/17041344.html