GCD

题目大意

ni=1d|igcd(d,id)

数据范围n<= 1011

莫比乌斯反演

题目相当于求

ni=1[ni]j=1gcd(i,j)

很像一道经典莫比乌斯反演题了。
设f(k)表示gcd(i,j)=k的对数,设g(k)表示k|gcd(i,j)的对数。
于是有

f(k)= [nk]i=1μ(i)g(ik)

所以问题变成了求

nk=1k[nk]i=1μ(i)g(ik)

我们发现g并不能O(1)得出,然后就连O(n)都做不到了,但是别急。
我们设T=i*k,然后交换主体,于是原式就等于

nT=1g(T)i|Tμ(i)Ti

显然当T> n 时g的值都为0所以T只需要枚举到 n
我们发现后面的部分只跟T有关系,所以可以预处理出来,用a(T)表示
然后,我们得到了一个十分优美的式子

nT=1g(T)a(T)

现在问题的关键变成怎样求g了。

g(k)= [nk]i=1[nik2]

所以我们可以分块O( nk )计算g(k),于是总复杂度就是O( nk=1nk )=O( n34 )(由于本人太弱,也不知道这个怎么算出来的)。
同时我们发现 [nik2] 很快就会返回0。
于是当它为0时,就可以退出计算当前的g(k),这样就可以跑得挺快了。

另外

该题有很多解法,本人比较蠢,只会莫比乌斯反演,出题人的方法简洁高效,
然而我没听懂(我太弱鸡了)。

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=316227;
ll a[maxn+5],mu[maxn+5],c[maxn+5];
bool bz[maxn+5];
int main()
{
    ll n,m,ans=0;
    scanf("%lld",&n);
    m=sqrt(n);
    mu[1]=1;
    for (int i=2;i<=m;i++){
        if (!bz[i]){
            mu[i]=-1;
            c[++c[0]]=i;
        }
        for (int j=1;j<=c[0];j++){
            if (i*c[j]>m) break;
            bz[i*c[j]]=1;
            if (i%c[j]==0){
                mu[i*c[j]]=0;
                break;
            }
            mu[i*c[j]]=-mu[i];
        }
    }
    for (int i=1;i<=m;i++) if (mu[i]!=0){
        for (int j=1;i*j<=m;j++) a[i*j]+=mu[i]*j;
    }
    for (int k=1;k<=m;k++){
        ll m1=n/k,l=1,r=0,x=0,m2=m1/k,s=0;
        while (l<=m1){
            x=m2/l;
            if (x==0) break;
            r=m2/x;
            s+=(r-l+1)*x;
            l=r+1;
        }
        ans+=s*a[k];
    }
    printf("%lld\n",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值