洛谷 P1829 [国家集训队]Crash的数字表格 莫比乌斯反演

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/80048492

题目描述

今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数a和b,LCM(a, b)表示能同时整除a和b的最小正整数。例如,LCM(6, 8) = 24。

回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张N*M的表格。每个格子里写了一个数字,其中第i行第j列的那个格子里写着数为LCM(i, j)。一个4*5的表格如下:

1 2 3 4 5
2 2 6 4 10
3 6 3 12 15
4 4 12 4 20
看着这个表格,Crash想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当N和M很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和mod20101009的值。

输入输出格式

输入格式:
输入的第一行包含两个正整数,分别表示N和M。

输出格式:
输出一个正整数,表示表格中所有数的和mod20101009的值。

输入输出样例

输入样例#1: 复制
4 5
输出样例#1: 复制
122
说明

30%的数据满足N, M≤ 10^3。

70%的数据满足N, M≤ 10^5。

100%的数据满足N, M≤ 10^7。

分析:洛谷题解里有的,讲得挺详细。
注意:反演时设的f[x]和g[x]要把所有与[gcd(i,j)==d]的相关项全设进去,比如这题的
f[x]=i*j*[gcd(i,j)==d]
g[x]=i*j*[x|gcd(i,j)]

于是就是f[x]=mul[x]*g[n/x]
在dalao指点下终于知道g怎么求了

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long

const LL mod=20101009;
const LL maxn=10000007;

using namespace std;

LL ans;
LL not_prime[maxn],prime[maxn];
LL mul[maxn];
LL n,m,cnt;

void getmul(LL n)
{
    mul[1]=1;
    for (LL i=2;i<=n;i++)
    {
        if (!not_prime[i])
        {
            prime[++cnt]=i;
            mul[i]=-1;
        }
        for (LL j=1;j<=cnt;j++)
        {
            if (i*prime[j]>n) break;
            not_prime[i*prime[j]]=1;
            if (i%prime[j]==0)
            {
                mul[i*prime[j]]=0;
                break;
            }
            mul[i*prime[j]]=-mul[i];
        }
    }
    for (LL i=1;i<=n;i++) mul[i]=(mul[i-1]+(mul[i]*i*i)%mod)%mod;
}

LL getsum(LL l,LL r)
{
    return (l+r)*(r-l+1)%mod*10050505%mod;
} 

LL g(int n,int m)
{
    if (n>m) swap(n,m);
    LL ans=0,last=0;    
    for (LL i=1;i<=n;i=last+1)
    {
        last=min(n/(n/i),m/(m/i));
        ans=(ans+getsum(1,n/i)*getsum(1,m/i)%mod*(mul[last]-mul[i-1]+mod)%mod)%mod;
    }
    return ans;
}

int main()
{
    getmul(maxn);
    scanf("%lld%lld",&n,&m);
    if (n>m) swap(n,m);             
    for (LL i=1,last=0;i<=n;i=last+1)
    {
        last=min(n/(n/i),m/(m/i));
        ans=(ans+getsum(i,last)*g(n/i,m/i)%mod)%mod;
    }
    printf("%lld",ans);
}
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页