spoj 4491 PGCD - Primes in GCD Table (莫比乌斯反演)

题目链接:哆啦A梦传送门

题意:给出n,m,求 ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) 为 素 数 ] \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)为素数] i=1nj=1m[gcd(i,j)]
n , m &lt; = 1 e 7 n,m&lt;=1e7 n,m<=1e7

依题意得:
s u m = ∑ p ∈ 素 数 ∑ i = 1 n ∑ j = 1 m g c d ( i , j ) = p \begin{aligned} sum&amp;=\sum_{p\in素数}\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)=p\\ \end{aligned} sum=pi=1nj=1mgcd(i,j)=p


这里我们用莫比乌斯反演第二描述去做。

F ( n ) = ∑ n ∣ d f ( d ) , f ( n ) = ∑ n ∣ d u ( d n ) F ( d ) F(n)=\sum_{n|d}f(d), f(n)=\sum_{n|d}u(\frac{d}{n})F(d) F(n)=ndf(d),f(n)=ndu(nd)F(d)


设 f(i)表示满足 g c d ( x , y ) = i gcd(x,y)=i gcd(x,y)=i时(x,y)的对数。
则F(i)表示满足 i ∣ g c d ( x , y ) i|gcd(x,y) igcd(x,y)时(x,y)的对数。我们易得: F ( i ) = ⌊ n i ⌋ ⌊ m i ⌋ F(i)=\left \lfloor \frac{n}{i} \right \rfloor \left \lfloor \frac{m}{i} \right \rfloor F(i)=inim

f ( i ) = ∑ i ∣ d u ( d i ) ⌊ n d ⌋ ⌊ m d ⌋ f(i)=\sum_{i|d}u(\frac{d}{i})\left \lfloor \frac{n}{d} \right \rfloor \left \lfloor \frac{m}{d} \right \rfloor f(i)=idu(id)dndm


即:我们直接枚举 d i \frac{d}{i} id的倍数d,得:
s u m = ∑ p ∈ 素 数 ∑ d = 1 ⌊ n p ⌋ u ( d ) ⌊ n p d ⌋ ⌊ m p d ⌋ \begin{aligned}sum=\sum_{p\in素数}\sum_{d=1}^{\left \lfloor \frac{n}{p} \right \rfloor}u(d)\left \lfloor \frac{n}{pd} \right \rfloor \left \lfloor \frac{m}{pd} \right \rfloor\end{aligned} sum=pd=1pnu(d)pdnpdm

这个式子复杂度为 O ( 质 数 个 数 ∗ n ) O(质数个数*\sqrt n) O(n ),复杂度还是有点大。

我们再令:
k = p d k=pd k=pd
那么代入得:
s u m = ∑ k = 1 n ∑ p ∈ 素 数 , p ∣ k u ( k p ) ⌊ n k ⌋ ⌊ m k ⌋ \begin{aligned}sum=\sum_{k=1}^{n}\sum_{p\in素数,p|k}u(\frac{k}{p})\left \lfloor \frac{n}{k} \right \rfloor \left \lfloor \frac{m}{k} \right \rfloor\end{aligned} sum=k=1np,pku(pk)knkm

f ( x ) = ∑ p ∈ 素 数 , p ∣ x u ( x p ) \begin{aligned}f(x)=\sum_{p\in素数,p|x}u(\frac{x}{p})\end{aligned} f(x)=p,pxu(px),那么
最终 s u m = ∑ k = 1 n f ( k ) ⌊ n k ⌋ ⌊ m k ⌋ \begin{aligned}sum=\sum_{k=1}^{n}f(k)\left \lfloor \frac{n}{k} \right \rfloor \left \lfloor \frac{m}{k} \right \rfloor\end{aligned} sum=k=1nf(k)knkm

我们只需在线性筛那里把 f ( x ) f(x) f(x)的前缀和弄出来就可以了。

线性筛方程:
f ( x ) = ∑ p ∈ 素 数 , p ∣ x u ( x p ) , 设 x 的 最 小 质 因 子 为 y , x = i ∗ y \begin{aligned}f(x)=\sum_{p\in素数,p|x}u(\frac{x}{p}),设x的最小质因子为y,x=i*y\end{aligned} f(x)=p,pxu(px),xyx=iy

u ( 1 ) , x 为 素 数 f ( x ) = u ( i ) , i % y = = 0 − f ( i ) + u ( i ) , i % y ! = 0 \begin{aligned} &amp;u(1),&amp;x为素数\\ f(x)=&amp;u(i), &amp;i\%y==0\\ &amp;-f(i)+u(i),&amp;i\%y!=0 \end{aligned} f(x)=u(1),u(i),f(i)+u(i),xi%y==0i%y!=0
详情见:神犇

那么我们就能在 O ( n ) O(\sqrt n) O(n )的时间复杂度解决。

代码:

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;

#define INF 0x3f3f3f3f
const LL mod=1e7+9;
const int N=1e7+10;

int prime[N],tot,mu[N];
bool vis[N];

LL f[N];///预处理函数f的前缀和


void init()
{
    mu[1]=1;

    for(int i=2;i<N;i++)
    {
        if(!vis[i]){
            prime[++tot]=i;
            mu[i]=-1;
            f[i]=1;
        }
        for(int j=1;j<=tot&&i*prime[j]<N;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0){

                f[i*prime[j]]=mu[i];
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
            f[i*prime[j]]=-f[i]+mu[i];


        }

        f[i]+=f[i-1];
    }

}

LL solve(int n,int m)
{

    LL re=0;

    int r;
    for(int l=1;l<=n;l=r+1){
        r=min(n/(n/l),m/(m/l));
        re=re+1LL*(f[r]-f[l-1])*(n/l)*(m/l);

    }
    return re;
}
int main()
{
    init();

    int ncase;
    scanf("%d",&ncase);

    LL n,m;
    while(ncase--)
    {
        scanf("%d%d",&n,&m);
        if(n>m) swap(n,m);

        printf("%lld\n",solve(n,m));
    }
    return 0;
}


预处理前缀和,还有另外一种方法:
参考博客:(https://www.cnblogs.com/zyb993963526/p/7253495.html)


#include<bits/stdc++.h>

using namespace std;
typedef long long LL;

#define INF 0x3f3f3f3f
const LL mod=1e7+9;
const int N=1e7+10;

int prime[N],tot,mu[N];
bool vis[N];

LL sum[N];///预处理函数f的前缀和


void init()
{
    mu[1]=1;

    for(int i=2;i<N;i++)
    {
        if(!vis[i]){
            prime[++tot]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=tot&&i*prime[j]<N;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0){

                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
    }

    ///预处理函数f的前缀和
    sum[0]=0;
    for(int i=1;i<=tot;i++)
    {
        for(int j=prime[i];j<N;j+=prime[i]){
            sum[j]+=mu[j/prime[i]];
        }
    }
    for(int i=1;i<N;i++){
        sum[i]+=sum[i-1];
    }


}

LL solve(int n,int m)
{

    LL re=0;

    int r;
    for(int l=1;l<=n;l=r+1){
        r=min(n/(n/l),m/(m/l));
        re=re+1LL*(sum[r]-sum[l-1])*(n/l)*(m/l);

    }
    return re;
}
int main()
{
    init();

    int ncase;
    scanf("%d",&ncase);

    LL n,m;
    while(ncase--)
    {
        scanf("%d%d",&n,&m);
        if(n>m) swap(n,m);

        printf("%lld\n",solve(n,m));
    }
    return 0;
}





我的标签:做个有情怀的程序员。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值