HDU 1695 (莫比乌斯入门)

题意:

从区间[1,B]选择一个x,从区间[1,D]选择一个数y,使得gcd(x,y)==k 的方案数。

莫比乌斯反演学习,感觉百度百科就非常好,其他的没有什么大的区别。

解题思路:

下面分两种办法进行求解。

首先,当然得用到莫比乌斯函数以及莫比乌斯反演了

1.采用莫比乌斯反演进行计算。

先将题意简化,那么gcd(x,y)==k就可以表达为:gcd(\frac{x}{k},\frac{y}{k})==1

那么求前者的个数就相当于求后者的个数。

设:

f(k)表示gcd(x,y)==k的个数

F(k)表示gcd(x,y)== (  k 的倍数 )  的个数

x\epsilon [1,B],y\epsilon [1,D]

为什么要这么设呢,

一方面:因为当k==1f(k)就是所要求解的答案

另一方面:便于使用下面这两个公式:

F(n)=\sum_{n|d}f(d)                             (1)

f(n)= \sum_{n|d}\mu (\frac{d}{n})F(d)                     (2)

对于公式(1)如果按照上面那种方法进行设,那么F(n)计算的结果就是(n的倍数的个数)。

并且你会发现F(n)=\left \lfloor \frac{B}{n} \right \rfloor\times \left \lfloor \frac{D}{n} \right \rfloor                   (3)

再带入公式(2)先将(3)中F中的n换为d,再令f中的n==1即可计算得到答案。

f(k)=\sum_{k|d}\mu (\frac{d}{k})\times \left \lfloor \frac{B}{d} \right \rfloor\times \left \lfloor \frac{D}{d} \right \rfloor

令k==1即可算得。

2.采用莫比乌斯进行计算。

因为    \sum_{d|n}\mu (d)=[n=1]

那么将n换成gcd(i,j)=1,可得:

\sum_{d|gcd(i,j)}\mu (d)=[gcd(i,j)=1]

那么,可以看出来:

\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=1]      (n<m)

直接套公式:

\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|gcd(i,j)}\mu (d)

然后枚举d,显然有d\in(1,n)\ \ \ (d|gcd(i,j))

于是将d提到前面去,则i,j都是d的倍数,化简一下,得

\sum_{d=1}^{n}\mu (d)\times \left \lfloor \frac{B}{d} \right \rfloor\times \left \lfloor \frac{D}{d} \right \rfloor

那么现在既可以计算答案了,用整除分块再进行优化

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn=1e5+10;

bool vis[maxn];
int prime[maxn];
int mu[maxn];
int sum[maxn];

void get_mu(){
    memset(vis,0,sizeof vis);
    mu[1]=1;
    int tot=0;
    for(int i=2;i<maxn;i++){
        if(!vis[i]){
            prime[tot++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<tot&&i*prime[j]<maxn;j++){
            if(i*prime[j]>maxn) break;
            vis[i*prime[j]]=1;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;
                break;
            }else{
                mu[i*prime[j]]=-mu[i];
            }
        }
    }
    for(int i=1;i<maxn;i++)
        sum[i]=sum[i-1]+mu[i];
}

ll solve(int x,int y){
    int lit=min(x,y);
    ll ans=0;
    for(int i=1,j;i<=lit;i=j+1){
        j=min(x/(x/i),y/(y/i));
        if(j>lit) j=lit;
        ans+=ll(sum[j]-sum[i-1])*(x/i)*(y/i);
    }
    return ans;
}

int main(){
    get_mu();
    int t,a,n,b,m,k;
    scanf("%d",&t);
    int Case=1;
    while(t--){
        scanf("%d%d%d%d%d",&a,&n,&b,&m,&k);
        ll ans=0;
        if(k){
            if(n>m) swap(n,m);
            ans=solve(n/k,m/k)-solve(n/k,n/k)/2;
        }
        printf("Case %d: %lld\n",Case++,ans);
    }
    return 0;
}
//

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值