[codeforces870F] Paths

题目大意

对于一个n个节点的图,节点编号为1到n,对于两个节点u,v,如果它们的gcd大于1,那么u与v之间有一条长度为1的边。
给定n,求节点两两之间距离之和(不连通则距离视为0)

n≤ 107

分析

首先容易发现,两个端点如果有一个编号为1或为大于 n2 的质数的点,那么这两点间一定没有路径。
接着两两间有路径的点对分以下几种情况:
1. 编号相同,距离为0
2. gcd大于1,距离为1
3. gcd等于1,设p[i]表示i的约数中的最小质数,当p[u]*p[v]≤n,时,可以构造出一条路径 u>2p[v]>v ,长度为2
4. gcd等于1,p[u]*p[v]>n,可以构造路径 p>2p[u]>2p[v]>v ,长度为3

接下来就是统计每种情况的点对数了。
第二种情况可以用欧拉函数算。
对于第三、四中情况,可以算出它们的总和,然后统计第三种情况有多少个。
总和可以用欧拉函数算。
第三中情况又分两种:
1. u,v都是合数,那么枚举u,可能的v个数为 ϕ(u)u+u1
2. u,v有至少一个质数,那么枚举u并令它为质数,可能的v个数为满足 p[u]p[v]n 的v个数减满足的v中u的倍数。这样子u,v都是质数的情况还会被算两次,要减重。

时间复杂度 O(n)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=1e7+5,M=4005;

typedef long long LL;

int n,tot,p[N],phi[N],sum[N],f[N],pc[N],cnt[N];

LL ans,now;

bool bz[N];

int main()
{
    scanf("%d",&n);
    for (int i=2;i<=n;i++)
    {
        if (!bz[i]) p[tot++]=i,phi[i]=i-1,f[i]=i,pc[i]=1;
        cnt[i]=cnt[i-1]+(1^bz[i]);
        sum[f[i]]++;
        for (int j=0;j<tot && i*p[j]<=n;j++)
        {
            bz[i*p[j]]=1; f[i*p[j]]=p[j];
            if (i%p[j]==0)
            {
                phi[i*p[j]]=phi[i]*p[j]; pc[i*p[j]]=pc[i]; break;
            }
            phi[i*p[j]]=phi[i]*(p[j]-1); pc[i*p[j]]=pc[i]+1;
        }
    }
    for (int i=2;i<=n;i++) sum[i]+=sum[i-1];
    now=1ll*sum[n/2]*(sum[n/2]-1)/2;
    for (int i=2;i<=n;i++) if (bz[i])
    {
        ans+=i-phi[i]-1; now-=i-phi[i]-1;
        ans+=2*(phi[i]-cnt[i]+pc[i]-1); now-=phi[i]-cnt[i]+pc[i]-1;
    }
    for (int i=2;i<=n/2;i++) if (!bz[i])
    {
        ans+=2*(sum[n/i]-n/i); now-=sum[n/i]-n/i;
        ans-=2*cnt[min(i-1,n/i)]; now+=cnt[min(i-1,n/i)];
        if (i>n/i) ans+=2*(sum[i]-sum[i-1]),now-=sum[i]-sum[i-1];
    }
    ans+=3*now;
    printf("%I64d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值