HDU 6390 多校第7场E题 数论+莫比乌斯反演+逆元

2 篇文章 0 订阅
1 篇文章 0 订阅

题意:给出函数,求值,取模

推导过程:

细节详见代码。

#include <iostream>

#include <string>

#include <algorithm>

#include <cstdio>

#include <queue>

#include <cstring>

#include <vector>

#include <climits>

using namespace std;

#define LL long long

const int maxn=1000005;

long long f[maxn],pre[maxn],inv[maxn];

int phi[maxn],u[maxn],prime[maxn],p;

bool vis[maxn];



void init1(){

    memset(vis, 0, sizeof vis);

    memset(u,0,sizeof u);

    for(int i=2;i<maxn;i++) if(!vis[i])

        for(int j=i;j<maxn;j+=i){

            if(u[j]==-1) continue;

            if((j/i)%i==0) u[j]=-1;

            else u[j]++;

            vis[j]=1;

        }

    for(int i=1;i<maxn;i++){

        if(u[i]==-1) u[i]=0;

        else if(u[i]%2) u[i]=-1;

        else u[i]=1;

    }

}

void init2(){

    int tot=0;

    phi[1]=1;

    for(int i=2;i<maxn;i++){

        if(!phi[i]){

            phi[i]=i-1;

            prime[tot++]=i;

        }

        for(int j=0;j<tot && prime[j]*i*1LL<maxn;j++){

            if(i%prime[j]) phi[i*prime[j]]=phi[i]*(prime[j]-1);

            else{

                phi[i*prime[j]]=phi[i]*prime[j];

                break;

            }

        }

    }

}

LL ans;

int n,m;

int main(){

    init1(); //Mobius G(x)系数打表

    init2(); //欧拉打表 复杂度O(nlogn)

    int _; cin>>_;

    while(_--){

        ans=0;

        scanf("%d%d%d",&n,&m,&p);

        inv[1] = 1;

        for (int i=2; i<=n; ++i) {

            inv[i] = 1LL* (p - p / i) * inv[p%i] % p;  //线性逆元表 O(n)复杂度

        }

        int N=min(m,n); //gcd(a,b)范围为1~min(a,b)

        for(int i=1;i<=N;i++) f[i]=1LL*(m/i)*(n/i); //预处理

        memset(pre,0,sizeof pre);

        for(int i=1;i<=N;i++){

            for(int j=i;j<=N;j+=i){

                pre[i]+=1ll*(f[j]*u[j/i]); //二重预处理

            }

        }

        for(int i=1;i<=N;i++) pre[i]%=p;//统一取模,由于u[]震荡性,不会溢出

        ans=0;

        for(int i=1;i<=N;i++){

            //将每一个gcd(a,b)==i的对数进行累加

            ans=(1LL*ans+1LL*i*inv[phi[i]]%p*pre[i]%p)%p;

        }

        //防止意外

        while(ans<0) ans+=p;

        printf("%lld\n",ans);

    }

    return 0;

}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值