[HDU](6390)GuGuFishtion ---- 欧拉函数★ + 容斥原理

题目链接

做法:

  • 首先根据唯一分解定理可知道,每个正整数都可以分解成质数幂的成绩
  • 然后我们带入到原式中去

(原谅我的字不好看(T▽T))

  • 我们就可以推出结论
  • Gu(a,b)=\frac{\varphi(a*b)}{\varphi(a)*\varphi(b)}=\frac{gcd(a,b)}{\varphi(gcd(a,b))}
  • 听了dls的直播讲解后,说一下我的理解:
  • 我们知道原暴力求答案复杂度是O(n*m)
  • 但是转换成结论后,我们可以根据gcd的性质来求解
  • 我们知道gcd(a,b) = d, d一定是a,b的公共因子,也一定能写成质因数分解的形式。
  • 且d 的值一定在[1, min(a,b)] 
  • 这时候我们可以通过枚举min(a,b)的因子来求解。咱么求呢,没错容斥一下
  • 这也是我想不到的地方,%%  dls
  • 这里我们设f[d] 表示的是gcd(a,b) = d 的倍数   的对数  (不是数学的“对数”,是几对数的对数!)
  • 那么这个对数怎么求呢?欧拉函数的求解过程没忘吧(~ ̄▽ ̄)~ 
  • 当当~   f[d] = (a/d)*(b/d)  (懂欧拉函数求解的小伙伴一定能看懂,就不多说啦)
  • F[d] 表示的是gcd(a,b) = d 的对数
  • 显然F[d] = f[d] - f[2*d] - f[3*d] …… f[n*d]  (容斥一下)
  • 这里的n*d是什么呢,是min(a,b)呀,因为它最大也就是min(a,b)
  • 所以对于每一个d, 结果就是  ans = d*inv(phi(d))%mod  ヾ(✿゚▽゚)ノ

 

AC代码:

#include<bits/stdc++.h>
#define rep(i,s,t) for(int i = (int)(s); i <= (int)(t); i++)
#define rev(i,t,s) for(int i = (int)(t); i >= (int)(s); i--)
#define pb(x) push_back(x)
#define all(x) x.begin(),x.end()
#define sz(x) (int)(x).size()
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const double PI = 4*atan(1.0);
const int maxn = 1e6+5;
const int INF = 0x3f3f3f3f;
int prime[maxn];
int phi[maxn];
ll inv[maxn];
ll f[maxn];
int tot,m,n,p;
int mmin,ans;
void phi_table()
{
    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 && 1LL*i*prime[j]<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;
            }
        }
    }
}
void inv_table()
{
    inv[1] = 1;
    for(int i=2;i<=mmin;i++)
       inv[i] = 1LL*(p-p/i)*inv[p%i]%p;
}
int main()
{
    #ifdef LOCAL_FILE
    freopen("in.txt","r",stdin);
    #endif // LOCAL_FILE
    ios_base::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    phi_table();
    int t;
    cin>>t;
    while(t--)
    {
        cin>>m>>n>>p;
        mmin = min(m,n);
        inv_table();
        ans = 0;
        for(int i=mmin;i>=1;i--)
        {
            f[i] = (1LL)*(n/i)*(m/i);
            for(int j=i+i;j<=mmin;j+=i) f[i]-=f[j];
            ans=(ans%p+i*f[i]%p*inv[phi[i]]%p)%p;
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值