Luogu2257 YY的GCD

原题链接:https://www.luogu.org/problemnew/show/P2257

YY的GCD

题目描述

神犇YY虐完数论后给傻×kAc出了一题

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

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

多组输入

输入输出格式
输入格式:

第一行一个整数T 表述数据组数

接下来T行,每行两个正整数,表示N, M

输出格式:

T行,每行一个整数表示第i组数据的结果

输入输出样例
输入样例#1:

2
10 10
100 100

输出样例#1:

30
2791

说明

T = 10000

N, M <= 10000000

题解

日常推式子。。。

p p 为素数, P P 表示素数集合:

ans=Pi=1nj=1m[gcd(i,j)=p] a n s = ∑ P ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = p ]

日常提出 p p

ans=Pi=1npj=1mp[gcd(i,j)=1] a n s = ∑ P ∑ i = 1 ⌊ n p ⌋ ∑ j = 1 ⌊ m p ⌋ [ g c d ( i , j ) = 1 ]

日常替换 [gcd(i,j)=1] [ g c d ( i , j ) = 1 ]

ans=Pi=1npj=1mpd|gcd(i,j)μ(d) a n s = ∑ P ∑ i = 1 ⌊ n p ⌋ ∑ j = 1 ⌊ m p ⌋ ∑ d | g c d ( i , j ) μ ( d )

日常枚举 d d

ans=d|gcd(i,j)μ(d)Pi=1npj=1mp[d|gcd(i,j)] a n s = ∑ d | g c d ( i , j ) μ ( d ) ∑ P ∑ i = 1 ⌊ n p ⌋ ∑ j = 1 ⌊ m p ⌋ [ d | g c d ( i , j ) ]

日常枚举 d d 的倍数:

ans=Pd=1min(np,mp)μ(d)i=1ndpj=1mdp a n s = ∑ P ∑ d = 1 m i n ( ⌊ n p ⌋ , ⌊ m p ⌋ ) μ ( d ) ∑ i = 1 ⌊ n d p ⌋ ∑ j = 1 ⌊ m d p ⌋

ans=Pd=1min(np,mp)μ(d)ndpmdp a n s = ∑ P ∑ d = 1 m i n ( ⌊ n p ⌋ , ⌊ m p ⌋ ) μ ( d ) ⌊ n d p ⌋ ⌊ m d p ⌋

T=dp T = d p ,代入:

ans=Pd=1min(np,mp)μ(Tp)nTmT a n s = ∑ P ∑ d = 1 m i n ( ⌊ n p ⌋ , ⌊ m p ⌋ ) μ ( T p ) ⌊ n T ⌋ ⌊ m T ⌋

改为枚举 T T

ans=T=1min(n,m)p|Tμ(Tp)nTmT a n s = ∑ T = 1 m i n ( n , m ) ∑ p | T μ ( T p ) ⌊ n T ⌋ ⌊ m T ⌋

f(T)=p|Tμ(Tp),T=i×p[j] f ( T ) = ∑ p | T μ ( T p ) , T = i × p [ j ] ,考虑线筛:

1.当 T T 本身为素数时,显然有 f(T)=μ(1)=1 f ( T ) = μ ( 1 ) = 1

2.当 T T 拥有多个最小质因子(即 i mod p[j]=0 i   m o d   p [ j ] = 0 )时:

(1)当 i i 本身无多个相同质因子时,那么当且仅当我们枚举的素数等于 p[j] p [ j ] 时, μ(Tp) μ ( T p ) 的值不为 0 0 ,此时 p=p[j],T=i×p[j] p = p [ j ] , T = i × p [ j ] ,所以有 μ(Tp)=μ(i) μ ( T p ) = μ ( i )

(2)当 i i 本身就是拥有多个因子的数时,无论我们怎么枚举, μ(Tp) μ ( T p ) 的值都为 0 0 ,此时,仍然有 μ(Tp)=μ(i) μ ( T p ) = μ ( i )

3.当 T T 的最小质因子只有一个时:

此时, T T 就比 i i 多了一个质因数 p[j] p [ j ] ,因为有:

f(i)=p|iμ(ip)f(T)=p|Tμ(i×p[j]p) f ( i ) = ∑ p | i μ ( i p ) f ( T ) = ∑ p | T μ ( i × p [ j ] p )

因为 i i 中没有质因数 p[j] p [ j ] ,所以有 μ(i×p[j]p)=μ(ip) μ ( i × p [ j ] p ) = − μ ( i p ) ,在此基础上, T T 还多了一个 μ(i×p[j]p[j])=μ(i) μ ( i × p [ j ] p [ j ] ) = μ ( i ) 。可得, f(T)=f(i)+μ(i) f ( T ) = − f ( i ) + μ ( i )

综上,我们得到线筛方程:

f(T)=μ(1)μ(i)f(i)+μ(i)TPi mod p[j]=0i mod p[j]0 f ( T ) = { μ ( 1 ) T ∈ P μ ( i ) i   m o d   p [ j ] = 0 − f ( i ) + μ ( i ) i   m o d   p [ j ] ≠ 0

再求一下 f(T) f ( T ) 的前缀和,利用下底分块,我们就可以 O(n) O ( n ) 解决询问了。

代码
#include
#define R register int
using namespace std;
const int M=1e7+5,N=1e7;
int p[M],miu[M],f[M],T;
bool check[M];
void get()
{
    R i,j,t;
    check[1]=miu[1]=1;
    for(i=2;i<=N;++i)
    {
        if(!check[i])f[i]=1,miu[i]=-1,p[++p[0]]=i;
        for(j=1;j<=p[0];++j)
        {
            t=i*p[j];if(t>N)break;
            check[t]=1;
            if(i%p[j]==0){f[t]=miu[i];miu[t]=0;break;}
            miu[t]=-miu[i],f[t]=miu[i]-f[i];
        }
        f[i]+=f[i-1];
    }
}
long long ans;
void query(int n,int m)
{
    R l,r;ans=0;
    if(n>m)swap(n,m);
    for(l=1;l<=n;l=r+1)r=min(n/(n/l),m/(m/l)),ans+=1ll*(f[r]-f[l-1])*(n/l)*(m/l);
}
void in(){get();scanf("%d",&T);}
void ac()
{
    R i,a,b;
    for(i=1;i<=T;++i)
    scanf("%d%d",&a,&b),query(a,b),printf("%lld\n",ans);
}
int main()
{
    in();ac();
    return 0;
}

你可能感兴趣的:(数论&数学========,莫比乌斯反演)