GDSOI 2016 T1 互补约数

Description

i=1nd|igcd(d,id)

n<=10^11

Solution

首先,我们发现gcd中的两个东西是所有乘积不超过n的数对,即

Ans=ij,ij<=ngcd(i,j)

然后
Ans=i=1nj=1nigcd(i,j)

咦?
长得那么像莫比乌斯的经典形式。只不过j的上界是会变化的。
那么我们可以尝试一下。
F(d) 表示上式中 gcd(i,j)=d 的个数, G(d) 表示 d|gcd(i,j) 的个数。
根据反演,
F(d)=i=1ndG(id)μ(i)

那么
Ans=d=1nF(d)d

展开
Ans=d=1ndi=1ndG(id)μ(i)

考虑枚举 T=id
Ans=T=1nG(T)i|TTiμ(i)

似乎复杂度一点都没有优化?
我们考虑一下如何求G
如果两个数的gcd=T,那么这两个数可以表示成XT,YT.
那么我们可以枚举X,
G(T)=x=1nT2nT2x

这个东西的复杂度大概是 O(N12)
由于n的下面有个平方,于是这个东西的复杂度肯定没问题。
于是发现原来的式子中,T只需要枚举到 n 就好了。
那么我们可以考虑预处理出 i|TTiμ(i) 设为 a(T)
那么原式就变成了优美的
Ans=T=1nG(T)a(T)

时间复杂度大约为 O(N34)
实际远远不到,O(跑得过)O(∩_∩)O~

Code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 1000005
using namespace std;
typedef long long ll;
ll a[N],p[N],n,ans;
int mu[N],Mx;
bool bz[N];
int main() {
    freopen("gcd.in","r",stdin);
    freopen("gcd.out","w",stdout);
    scanf("%lld",&n);mu[1]=1;Mx=sqrt(n);
    fo(i,2,Mx) {
        if (!bz[i]) p[++p[0]]=i,mu[i]=-1;
        fo(j,1,p[0]) {
            ll k=i*p[j];if (k>Mx) break;
            bz[k]=1;if (!(i%p[j])) break;
            mu[k]=-mu[i];
        }
    }
    fo(i,1,Mx) fo(j,1,Mx/i) a[i*j]+=mu[i]*j;
    fo(i,1,Mx) {
        ll m=n/(ll)i/(ll)i,sum=0;
        for(ll l=1,r=0;l<=m;l=r+1) 
        r=m/(m/l),sum=sum+(r-l+1)*(m/l);
        ans=ans+a[i]*sum;
    }
    printf("%lld",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值