所有点对最短路径问题(S2oj337)

标签: 线性筛,欧拉函数

对于 gcdgcd 不为 11 的点对,距离显然为 11

否则考虑寻找过渡点。这里设 pxp_x 表示 xx 的最小质因子,可以通过线性筛得到

  1. 只有一个过渡点(距离为 22):

    ​ 那这个过渡点和两点的 gcdgcd 都不为 11,显然 pxpyp_xp_yxxyy 之间的最小过渡点, 所以两点距离为 22 当且仅当 pxpyngcd(x,y)=1p_xp_y \leq n \land \gcd(x,y) = 1

  2. 有两个过渡点(距离为 33) :

    ​ 这两个点的距离既不 11 又不是 22 ,所以 px×py>ngcd(x,y)=1p_x\times p_y > n \land gcd(x, y) = 1, 而这两个过渡点最小肯定是 2px2p_x2py2p_y 所以两点距离为 33 当且仅当 px×py>ngcd(x,y)=12pxn2pynp_x\times p_y > n \land gcd(x, y) = 1 \land 2p_x \leq n \land 2p_y \leq n .

  3. 有多个过渡点(距离为 3+3^+) :

    ​ 两个过渡点无法连接的点,更多的过渡点一定也无法连接,因为 2px2py2p_x 和 2p_y 为可以与两个点相连且可以互相相连的最小的点。

  4. 无法连接:

    ​ 那就是说 nn 以内不存在过渡点和这两个点相连,且这两个点不直接相连。即 2px>n2py>n2p_x > n \land 2p_y > n ,所以它一定是质数。px=xp_x = x

    如果他是合数的话 2pxxn2p_x \leq x \leq n 不符合条件

对于直接相连的点, 一共有 i=1nj=1i1[gcd(i,j)1]\sum_{i=1}^{n}\sum_{j=1}^{i-1} [\gcd(i,j) \neq 1]i=1nj=1i1[gcd(i,j)1]=n×(n1)2i=1nj=1i1[gcd(i,j)=1]=n×(n1)2i=2nϕ(i)\sum_{i=1}^{n}\sum_{j=1}^{i-1} [\gcd(i,j) \neq 1]\\ =\frac{n\times (n - 1)}{2} - \sum_{i=1}^{n}\sum_{j=1}^{i-1} [\gcd(i,j) = 1]\\ =\frac{n\times (n - 1)}{2} - \sum_{i=2}^{n}\phi(i)

对于距离为 22 的点,我不是很会求, 但是用总点对数减去其他三种情况就好了。

对于距离为 33 的点,px×py>ngcd(x,y)=12pxn2pyn p_x\times p_y > n \land gcd(x, y) = 1 \land 2p_x \leq n \land 2p_y \leq n 条件好多, 一个一个看。

考虑枚举 xxyy 的数量.

2pxn 2p_x \leq n 直接判断 xx 是否符合条件.

px×py>n2pynp_x\times p_y > n \land 2p_y \leq n 我们得到 npx<pyn2\frac{n}{p_x} < p_y \leq \frac{n}{2}, 可以预处理出这个范围内 pyp_y 的数量

gcd(x,y)=1gcd(x, y) = 1 如果要满足这个条件,满足要求的 pyp_y 就不会在一段连续的区间内, 但是 px×py>np_x \times p_y > n , 假设gcd(x,y)=pzgcd(x, y) = p_z 那么 pzpxpzpyp_z \geq p_x \land p_z \geq p_y , 所以 px×pz>npy×pz>np_x \times p_z > n \land p_y \times p_z > n 不符合题意. 所以只要px×py>np_x \times p_y > ngcd(x,y)gcd(x, y) 就等于 11, 但是注意排除px=pyp_x = p_y 的情况 我们只要在 npx<pxn2\frac{n}{p_x} < p_x \leq \frac{n}{2} 时减去 pxp_x 的数量即可

所以我们只需要满足 2pxnpx×py>n2pynpxpy2p_x \leq n \land p_x\times p_y > n \land 2p_y \leq n \land p_x \neq p_y,

对于无法连接的点对, 预处理出关于质数个数的前缀和 sumsum。点对数为n1+i2i>nnn1(sumisumn2)n - 1 + \sum_{i是质数\land 2i > n}^n n - 1 - (sum_i - sum_\frac{n}{2}), 11 和除它以外的任何数 gcdgcd 都是 11.

Code :

#include <iostream>
#include <cstdio>
#define ll long long

using namespace std;

const int N = 1e7 + 10;
ll len[5];
int vis[N], phi[N], pri[N], tot, p[N], nump[N];
int sum[N];
int n;

void euler(int n)
{
    phi[1] = 1;
    vis[1] = 1;
    for (int i = 2; i <= n; ++i)
    {
        if (!vis[i]) pri[++tot] = i, phi[i] = i - 1, p[i] = i;
        for (int j = 1; j <= tot && pri[j] * i <= n; ++j)
        {
            vis[i * pri[j]] = 1;
            p[i * pri[j]] = pri[j];
            if (i % pri[j] == 0)
            {
                phi[i * pri[j]] = phi[i] * pri[j];
                break;
            }
            phi[i * pri[j]] = phi[i] * phi[pri[j]];
        }
    }
}

int main()
{
    scanf("%d", &n);
    // 特判
    if (n == 1)
    {
        cout << 0 << endl;
        return 0;
    }
    euler(n);
    for (int i = 2; i <= n; ++i)
    {
        len[1] += 1ll * phi[i];
    }
    len[1] = 1ll * n * (n - 1) / 2 - len[1];
    for (int i = 1; i <= n; ++i)
    {
        sum[i] = sum[i - 1] + (vis[i] ^ 1);
    }
    int s = 0;
    for (int i = n; i > n / 2; --i)
    {
        if (!vis[i]) len[0] += n - 1 - (sum[i] - sum[n / 2]);
    } 
    len[0] += n - 1;
    for (int i = 1; i <= n; ++i)
    {
        ++nump[p[i]];
    }
    for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + nump[i];
    for (int i = 2; i <= n; ++i)
    {
        if (2 * p[i] <= n)
        {
            len[3] += sum[n / 2] - sum[n / p[i]];
            if (n / p[i] < p[i] && p[i] <= n / 2)
            {
                len[3] -= nump[p[i]];
            }
        }
    }
    len[3] /= 2;
    len[2] = 1ll * n * (n - 1) / 2 - len[1] - len[3] - len[0];
    cout << 1ll * len[3] * 3 + 1ll * len[2] * 2 + len[1] << endl;
    return 0;
}

pxp_x 的数量指最小质因数等于 pxp_x 的数的数量。