【BZOJ2154】Crash的数字表格,数论练习之二维LCM(莫比乌斯反演)


Time:2016.06.18
Author:xiaoyimi
转载注明出处谢谢
看不见的分割线
传送门
思路:
设n≤m
i=1nj=1mlcm(i,j)
=i=1nj=1mijgcd(i,j)
=i=1nj=1md=1n[d|i][d|j][gcd(i,j)=d]ijd
令i=di,j=dj
=d=1ni=1ndj=1md[gcd(i,j)=1]dij
=d=1ndi=1ndj=1mdp=1nd[p|i][p|j]μ(p)ij
=d=1ndp=1ndμ(p)p2i=1ndpj=1mdpij
=d=1ndp=1ndμ(p)p2[ndp][mdp]([ndp]+1)([mdp]+1)4
两次分块求解,复杂度为 O((n)2=n)
具体做法就是先通过n/d,m/d的取值对d分块,即在每一个块内对于不同的d来说,n/d,m/d的取值是分别相等的,然后再通过一个函数S(n,m)求得S(n/d,m/d),其中S函数也是可以分块求得的,此处不再赘述
S(n,m)
=i=1nj=1m[gcd(i,j)=1]ij
=d=1nμ(d)d2[nd][md]([nd]+1)([md]+1)4
注意:
μ(d)d2 可以处理前缀和;
有很多地方会炸int甚至long long
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#define M 10000005
#define mo 20101009
#define LL long long
using namespace std;
int n,m,ans;
int prime[M>>3],mu[M];
bool vis[M];
int cal(int n,int m)
{
    int up,ans=0;
    for (int i=1;i<=n;i=up+1)
    {
        up=min(n/(n/i),m/(m/i));
        ans=(ans+((LL)(n/i)*(n/i+1)/2%mo)*((LL)(m/i)*(m/i+1)/2%mo)%mo*(mu[up]-mu[i-1])%mo)%mo;
    }
    return ans;
}
main()
{
    scanf("%d%d",&n,&m);
    if (n>m) swap(n,m);
    mu[1]=1;
    for (int i=2;i<=m;i++)
    {
        if (!vis[i]) prime[++prime[0]]=i,mu[i]=-1;
        for (int j=1;j<=prime[0];j++)
            if (i*prime[j]<=m)
            {
                vis[i*prime[j]]=1;
                if (i%prime[j]) mu[i*prime[j]]=-mu[i];
                else {mu[i*prime[j]]=0;break;}
            }
            else break;
    }
    for (int i=2;i<=m;i++) mu[i]=(mu[i-1]+(LL)mu[i]*i*i%mo)%mo;
    int up;
    for (int i=1;i<=n;i=up+1)
    {
        up=min(n/(n/i),m/(m/i));
        ans=(ans+(LL)(up-i+1)*(up+i)/2%mo*cal(n/i,m/i)%mo)%mo;
    }
    printf("%d",(ans+mo)%mo);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值