所有点对最短路径问题(S2oj337)
标签: 线性筛,欧拉函数
对于 不为 的点对,距离显然为
否则考虑寻找过渡点。这里设 表示 的最小质因子,可以通过线性筛得到
- 
只有一个过渡点(距离为 ):
 那这个过渡点和两点的 都不为 ,显然 是 和 之间的最小过渡点, 所以两点距离为 当且仅当
 - 
有两个过渡点(距离为 ) :
 这两个点的距离既不 又不是 ,所以 , 而这两个过渡点最小肯定是 和 所以两点距离为 当且仅当 .
 - 
有多个过渡点(距离为 ) :
 两个过渡点无法连接的点,更多的过渡点一定也无法连接,因为 为可以与两个点相连且可以互相相连的最小的点。
 - 
无法连接:
 那就是说 以内不存在过渡点和这两个点相连,且这两个点不直接相连。即 ,所以它一定是质数。
如果他是合数的话 不符合条件
 
对于直接相连的点, 一共有 对
对于距离为 的点,我不是很会求, 但是用总点对数减去其他三种情况就好了。
对于距离为 的点, 条件好多, 一个一个看。
考虑枚举 找 的数量.
直接判断 是否符合条件.
我们得到 , 可以预处理出这个范围内 的数量
如果要满足这个条件,满足要求的 就不会在一段连续的区间内, 但是 , 假设 那么 , 所以 不符合题意. 所以只要, 就等于 , 但是注意排除 的情况 我们只要在 时减去 的数量即可
所以我们只需要满足 ,
对于无法连接的点对, 预处理出关于质数个数的前缀和 。点对数为, 和除它以外的任何数 都是 .
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;
}
的数量指最小质因数等于 的数的数量。

京公网安备 11010502036488号