GuGuFishtion (欧拉函数题 gcd=i的对数的处理方法)

原题:hdu 6390

题意:

定义 G(a,b)=ϕ(ab)ϕ(a)ϕ(b)(ϕ(i)), G ( a , b ) = ϕ ( a ∗ b ) ϕ ( a ) ∗ ϕ ( b ) ( ϕ ( i ) 为 欧 拉 函 数 ) , 求:

a=1n(b=1mG(a,b))(n,m[1,1e6]) ∑ a = 1 n ( ∑ b = 1 m G ( a , b ) ) ( n , m ∈ [ 1 , 1 e 6 ] )

解析:

欧拉函数的公式为:

ϕ(n)=n(11P1)(11P2)...(11Pn)(Pin) ϕ ( n ) = n ∗ ( 1 − 1 P 1 ) ∗ ( 1 − 1 P 2 ) ∗ . . . ∗ ( 1 − 1 P n ) ( P i 为 n 的 质 因 子 )

那么易得:

G(a,b)=ϕ(ab)ϕ(a)ϕ(b)=(11Q1)(11Q2)...(11Qn)(Qigcd(a,b)) G ( a , b ) = ϕ ( a ∗ b ) ϕ ( a ) ∗ ϕ ( b ) = ( 1 − 1 Q 1 ) ∗ ( 1 − 1 Q 2 ) ∗ . . . ∗ ( 1 − 1 Q n ) ( Q i 为 g c d ( a , b ) 的 质 因 子 )

那么我们要处理的就是 gcd(a,b)=i g c d ( a , b ) = i 时的 G(a,b) G ( a , b ) 值 ,以及有多少对a,b的gcd为i

一 : gcd(a,b)=i g c d ( a , b ) = i 时的 G(a,b) G ( a , b ) 值

G(a,b)=ϕ(i)/i, G ( a , b ) = ϕ ( i ) / i , 所以我们可以用预处理欧拉函数差不多的方法预处理出G值

for(D i=2;i<=MAXN;i++){// v为G值
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;//处理逆元
        if(!vis[i]){
            pri[++now]=i;
            v[i]=i*inv[i-1]%mod;//质数的G
        }
        for(D j=1;j<=now&&pri[j]*i<=MAXN;j++){
            vis[i*pri[j]]=1;
            if(i%pri[j]==0){
                v[i*pri[j]]=v[i];// i|pri[j]那么i*pri[j]的质因子==i的质因子
                break;
            }
            v[i*pri[j]]=v[i]*v[pri[j]]%mod;//i没包括pri[j]这个质因子的时候
        }
    }

二 : a[1,n],b[1,m](a,b)gcdi a ∈ [ 1 , n ] , b ∈ [ 1 , m ] 有 多 少 对 ( a , b ) 的 g c d 为 i

对于一个数i,在 a[1,n],b[1,m] a ∈ [ 1 , n ] , b ∈ [ 1 , m ] 的范围内, f[i]gcd(i,2i,3i...) 设 f [ i ] 为 g c d 为 ( i , 2 i , 3 i . . . ) 的 对 数

显然 : f[i]=[n/i][m/i] f [ i ] = [ n / i ] ∗ [ m / i ]

那么我们从大到小维护 f[i] f [ i ] ,因为我们要的是 gcd=i g c d = i 的对数,所以要把 gcd=2i g c d = 2 i 的情况减去


    for(D i=1;i<=MAXN;i++){
        f[i]=(n/i)*(m/i)%mod;
    }
    for(D i=MAXN/2;i>=1;i--){//最高到MAXAN/22i的情况
        for(D j=i+i;j<=MAXN;j+=i){
            f[i]-=f[j];
            if(f[i]<0)f[i]+=mod;
        }
    }


代码:


#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<vector>
#include<stack>
#include<queue>
#include<ctime>
#include<cstdlib>
#include<sstream>
#include<functional>
using namespace std;
#define D long long
#define F double
#define mmm(a,b) memset(a,b,sizeof(a))
D read(){ D ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
const int N=1000000;
const F pi=acos(-1);

D n,m,p,mi;
D f[N+9],v[N+9],inv[N+9];
D pri[N+9],now;
bool vis[N+9];
void init(){
    mmm(vis,0);mi=min(m,n);now=0;
    for(D i=1;i<=mi;i++){
        f[i]=(n/i)*(m/i)%p;
    }
    for(D i=mi/2;i>=1;i--){
        for(D j=i+i;j<=mi;j+=i){
            f[i]-=f[j];
            if(f[i]<0)f[i]+=p;
        }
    }
    v[1]=inv[1]=1;
    for(D i=2;i<=mi;i++){
        inv[i]=(p-p/i)*inv[p%i]%p;
        if(!vis[i]){
            pri[++now]=i;
            v[i]=i*inv[i-1]%p;
        }
        for(D j=1;j<=now&&pri[j]*i<=mi;j++){
            vis[i*pri[j]]=1;
            if(i%pri[j]==0){
                v[i*pri[j]]=v[i];
                break;
            }
            v[i*pri[j]]=v[i]*v[pri[j]]%p;
        }
    }
}

int main(){
    int t=read();
    while(t--){
        n=read(),m=read(),p=read();
        init();
        D ans=0;
        for(D i=1;i<=mi;i++)
            ans=(ans+f[i]*v[i])%p;
        printf("%lld\n",ans);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值