YY的GCD


Ac链接

  • 题目描述:神犇YY虐完数论后给傻×kAc出了一题

    ​ 给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对

    ​ kAc这种傻×必然不会了,于是向你来请教……

    ​ 多组输入(大概也就一万组吧……)

  • 解决本题需要了解莫比乌斯反演的知识,所以先来普及知识点(最为枯燥的部分,但忍下去)。

莫比乌斯函数
  • :莫比乌斯函数是啥啊?

    :(莫比乌斯是一个由容斥系数所构成的函数)。

  • μ(d)的定义:

    • 当d=1时,μ(d)=1;
    • 当d= Π i = 1 k p i \Pi_{i=1}^kp_i Πi=1kpi p i p_i pi为互质素数时,μ(d)= ( − 1 ) k (-1)^k (1)k。(说直白点,就是d分解质因数后,没有幂次大于平方的质因子,此时函数值根据分解的个数决定);
    • 只要当d含有任何质因子的幂大于等于2,则函数值为0。
  • 莫比乌斯函数的一些其他有趣的性质:

    • 对于任意正整数n, ∑ d ∣ n μ ( d ) \sum_{d|n}μ(d) dnμ(d)=[n=1]。(PS:这一条性质是莫比乌斯反演中最常用的)
    • 对于任意正整数n, ∑ d ∣ n μ ( d ) d \sum_{d|n} \frac{μ(d)}{d} dndμ(d)= φ ( n ) n \frac{φ(n)}{n} nφ(n)。(这个性质很奇妙,把欧拉函数与莫比乌斯函数结合起来,下次学杜教筛会证明)。
  • 求莫比乌斯函数的程序实现不难,只需要在线性筛的程序上略作修改,便可以筛出μ函数。

void mu_get(){
	mu[1]=1;
	for(int i=2;i<=n;++i){
		if(!v[i]){
			p[++m]=i,mu[i]=-1;
		}
		for(int j=1;j<=m;++j){
			if(p[j]>v[i]||i*p[j]>n) break;
			v[i*p[j]]=p[j];
			if(i%p[j]) break;
			else mu[i*p[j]]=-mu[i];
		}
	}
}
莫比乌斯反演
  • 解决了莫比乌斯函数的问题后,我们就迎来了恶心的莫比乌斯反演。

  • 莫比乌斯反演定理:F(n)和f(n)是定义在非负整数集合上的两个函数,并且满足条件:

    F ( n ) = ∑ d ∣ n f ( d ) F(n)=\sum_{d|n}f(d) F(n)=dnf(d)

    那么现在存在一个结论:

    f ( n ) = ∑ d ∣ n μ ( d ) F ( n d ) f(n)=\sum_{d|n}μ(d)F(\frac{n}{d}) f(n)=dnμ(d)F(dn)

  • 莫比乌斯反演的证明主要有两种方式,其中一种就是通过定义来证明;另一种利用狄利克雷卷积。先来说一说第一种证明方法:

    ∑ d ∣ n μ ( d ) F ( n d ) = ∑ d ∣ n μ ( d ) ∑ i ∣ n d f ( i ) \sum_{d|n}μ(d)F(\frac{n}{d})=\sum_{d|n}μ(d)\sum_{i|\frac{n}{d}}f(i) dnμ(d)F(dn)=dnμ(d)idnf(i)

    = ∑ i ∣ n f ( i ) ∑ d ∣ n i μ ( d ) = f ( n ) =\sum_{i|n}f(i)\sum_{d|\frac{n}{i}}μ(d)=f(n) =inf(i)dinμ(d)=f(n)

    (PS:如果不知道第二部如何推导第三部,可以考虑第二个式子枚举的i与d的关系,发现可以调换一下枚举的方式。如果不知道最后一步怎么来的,可以去看一下上面莫比乌斯函数的性质1)。
  • 当然,莫比乌斯反演有另外的一种形式,当F(n)和f(n)满足:

    F ( n ) = ∑ n ∣ d f ( d ) F(n)=\sum_{n|d}f(d) F(n)=ndf(d)

    可以推出:

    f ( n ) = ∑ n ∣ d μ ( d n ) F ( d ) f(n)=\sum_{n|d}μ(\frac{d}{n})F(d) f(n)=ndμ(nd)F(d)

  • 感觉这个式子,可能在莫比乌斯反演中更加好用。

回到本题
  • 显然,Ans= ∑ p ϵ p r i m ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = p ] \sum_{p\epsilon prim}\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=p] pϵprimi=1nj=1m[gcd(i,j)=p]

  • 对于这种和gcd有关的莫比乌斯反演,我们一般另f(d)为gcd(i,j)=d的个数,即f(d)= ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=d] i=1nj=1m[gcd(i,j)=d] 。那么F(n)为gcd(i,j)为n或者n的倍数的个数,即:

    F ( n ) = ∑ n ∣ d f ( d ) = ⌊ N n ⌋ ⌊ M m ⌋ F(n)=\sum_{n|d}f(d)=\lfloor\frac{N}{n}\rfloor\lfloor\frac{M}{m}\rfloor F(n)=ndf(d)=nNmM

    那么根据莫比乌斯反演定理:

    f ( n ) = ∑ n ∣ d μ ( d n ) F ( d ) f(n)=\sum_{n|d}μ(\frac{d}{n})F(d) f(n)=ndμ(nd)F(d)

  • 接下来就是化简式子了!

    A n s = ∑ p ϵ p r i m f ( p ) Ans=\sum_{p\epsilon prim}f(p) Ans=pϵprimf(p)

    = ∑ p ϵ p r i m ∑ p ∣ d μ ( d p ) F ( d ) =\sum_{p\epsilon prim}\sum_{p|d}μ(\frac{d}{p})F(d) =pϵprimpdμ(pd)F(d)

    换一个枚举项,枚举 ⌊ d p ⌋ \lfloor\frac{d}{p}\rfloor pd

    ∑ p ϵ p r i m ∑ d = 1 m i n ( ⌊ n p ⌋ , ⌊ m p ⌋ ) μ ( d ) F ( d p ) = ∑ p ϵ p r i m ∑ d = 1 m i n ( ⌊ n p ⌋ , ⌊ m p ⌋ ) μ ( d ) ⌊ n d p ⌋ ⌊ m d p ⌋ \sum_{p\epsilon prim}\sum_{d=1}^{min(\lfloor\frac{n}{p}\rfloor,\lfloor\frac{m}{p}\rfloor)}μ(d)F(dp)=\sum_{p\epsilon prim}\sum_{d=1}^{min(\lfloor\frac{n}{p}\rfloor,\lfloor\frac{m}{p}\rfloor)}μ(d)\lfloor\frac{n}{dp}\rfloor\lfloor\frac{m}{dp}\rfloor pϵprimd=1min(pn,pm)μ(d)F(dp)=pϵprimd=1min(pn,pm)μ(d)dpndpm

    这个dp看着很不爽,我们把它换成T

    A n s = ∑ T = 1 m i n ( n , m ) ∑ t ∣ T , t ϵ p r i m μ ( T t ) ⌊ n T ⌋ ⌊ m T ⌋ Ans=\sum_{T=1}^{min(n,m)}\sum_{t|T,t\epsilon prim}μ(\frac{T}{t})\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor Ans=T=1min(n,m)tT,tϵprimμ(tT)TnTm

    A n s = ∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ ⌊ m T ⌋ ∑ t ∣ T , t ϵ p r i m μ ( T t ) Ans=\sum_{T=1}^{min(n,m)}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{t|T,t\epsilon prim}μ(\frac{T}{t}) Ans=T=1min(n,m)TnTmtT,tϵprimμ(tT)

  • 这样就已经可以O(n)求了。不过这道题有多组数据,所以直接O(n)求会挂。所以我们利用前缀和的思路,打一个整除分块,就可以O(n)预处理,O( n \sqrt{n} n )求每一组数据了。

历经磨难终于A掉了这道题,哎数学题推式子我总是看着题解一遍一遍推,还是没有感觉,弱啊……

Coding
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e7+10;
int n,m,cnt,p[N],v[N],mu[N];ll sum[N],ans,g[N];
void get_mu(int a){
    mu[1]=1;
    for(int i=2;i<=a;++i){
        if(!v[i]){
            p[++cnt]=i;mu[i]=-1;
        }
        for(int j=1;j<=cnt;++j){
            if(p[j]*i>a) break;
            v[i*p[j]]=p[j];
            if(i%p[j]==0) break;
            else mu[i*p[j]]=-mu[i];
        }
    }
    for(int i=1;i<=cnt;++i){
        for(int j=1;j*p[i]<=a;++j) g[p[i]*j]+=mu[j];
    }
    for(int i=1;i<=a;++i) sum[i]=sum[i-1]+g[i];
}
int main(){
    get_mu(1e7);
    int t;scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        ans=0;
        if(n>m) swap(n,m);
        for(int l=1,r;l<=n;l=r+1){
            r=min((n/(n/l)),m/(m/l));
            ans+=(sum[r]-sum[l-1])*(n/l)*(m/l);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
感谢pengymdalao的精彩博客
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值