HDU-6390 GuGuFishtion(莫比乌斯反演)

题目 给出n,m,p求

思路:phi(a)=a*(1-1/p1)*(1-1/p2)*.....*(1-1/pn)    pi是a的素因子;

phi(a*b)=a*b*(1-1/p1)*(1-1/p2)*.....*(1-1/pm)里面包含了a和b的所有素因子(去重后的)。

phi(a*b) / (phi(a)*phi(b)) 约分后的结果就是1/[  (1-1/p1)*(1-1/p2)*...*(1-1/pv)  ]  其中p1,p2...pv是a和b的gcd

然后这个题就是求从1-n选一个x和1-m选一个y使得gcd(x,y)=k的组数,枚举每一个k,每次跑min(n,m)/k次,总复杂度是O(nlogn)。

前面要预处理以每个数作为gcd时的贡献。      (第一次比赛时做出来莫比乌斯反演的题目...之前都是gg的,好菜。。)

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000000;
bool check[MAXN+10];
int prime[MAXN+10];
int mu[MAXN+10];
void Mobius()
{
    memset(check,false,sizeof(check));
    mu[1] = 1;
    int tot = 0;
    for(int i = 2; i <= MAXN; i++)
    {
        if( !check[i] )
        {
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < tot; j++)
        {
            if(i * prime[j] > MAXN) break;
            check[i * prime[j]] = true;
            if( i % prime[j] == 0)
            {
                mu[i * prime[j]] = 0;
                break;
            }
            else
            {
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
}
#define ll long long
int b,d,t;
ll p;
ll inv[MAXN+10];
ll a[MAXN+10];
bool visit[MAXN+10];
void init(int N)
{
    int i,j,n=N;
    for(int i=0;i<=N;i++)
        visit[i]=true;
    for(int i=0;i<=N;i++)
        a[i]=1;
    for (i=2;i<=n;i++)
    {
        ll tmp=1ll*i*inv[i-1]%p;
        if (visit[i])
        {
            a[i]=a[i]*tmp%p;
            for (j=i+i;j<=n;j+=i)
              {
                a[j]=a[j]*tmp%p;
                visit[j]=false;
              }
        }
    }
}
int main()
{
    Mobius();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%I64d",&b,&d,&p);

        inv[0]=inv[1]=1;
        for(int i=2;i<=b;i++)
            inv[i]=1ll*(p-p/i)*inv[p%i]%p;
        init(b);

        if(b>d) swap(b,d);
        ll ans=0;
        for(int k=1;k<=b;k++)
        {
            int bb=b/k,dd=d/k;
            ll num=0;
            for(int i=1;i<=bb;i++)
                num+=(ll)mu[i]*(bb/i)*(dd/i);
            num%=p;
            ans+=num*a[k]%p;
            if(ans>p)
            ans-=p;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dllpXFire/article/details/81634999