最容易理解的莫比乌斯反演

本文探讨了如何解决特定条件下的数对(x, y)计数问题,利用莫比乌斯反演简化计算过程。通过数学算法求解给定区间内两数最大公约数等于特定值k的所有数对数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于给出的n个询问,每次求有多少个数对(x,y),满足axbcyd,且gcd(x,y) = kgcd(x,y)函数为xy的最大公约数

1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

Sample Input
2
2 5 1 5 1
1 5 1 5 2
Sample Output
14
3









上叙对于区间最远的端点的理解:

若a为10,x为 4 ,因为 10除以4有余数,所以会存在x在扩大的过程中尽量减少这个余数,很容易知道当x为5的时候刚好没有余数,同时他们的商都是2

最后发现,其实上面叙述的P 其实就是莫比乌斯函数

而莫比乌斯函数反演有两种形式,一般是第二种:


从这两个形式中可以发现莫比乌斯函数的两个应用:

对于一些函数,我们很难直接求他的值,但是很容易求这个函数的倍数或者约数的值

此时我们可以用莫比乌斯反演得到此函数,本题就是这么个道理





#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

#define MAXN 100000
#define LL long long
bool check[MAXN+10];
int primer[MAXN+10];
int mu[MAXN+10];
int sum[MAXN+10];

void Moblus()
{
    memset(check,0,sizeof(check));
    mu[1]=1;
    int tot=0;
    for(int i=2;i<=MAXN;i++){
        if(!check[i]){
            primer[tot++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<tot&&i*primer[j]<=MAXN;j++){
            check[i*primer[j]]=true;
            if(i%primer[j]==0){
                mu[i*primer[j]]=0;
                break;
            }
            mu[i*primer[j]]=-mu[i];
        }
    }
    sum[0]=0;
    for(int i=1;i<=MAXN;i++)
        sum[i]=sum[i-1]+mu[i];
}

LL solve(int n,int m)
{
    LL ans=0;
    if(n>m) swap(n,m);
    for(int i=1,last;i<=n;i=last+1){
        last=min(n/(n/i),m/(m/i));
        ans+=(LL)(sum[last]-sum[i-1])*(n/i)*(m/i);
    }
    return ans;
}

int main()
{
    int T;
    Moblus();
    int a,b,c,d,k;
    //freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);

        LL ans=solve(b/k,d/k)-solve((a-1)/k,d/k)
        -solve(b/k,(c-1)/k)+solve((a-1)/k,(c-1)/k);
        printf("%lld\n",ans);
    }
    return 0;
}



Given 5 integers: a, b, c, d, k, you're to find x in a...b, y in c...d that GCD(x, y) = k. GCD(x, y) means the greatest common divisor of x and y. Since the number of choices may be very large, you're only required to output the total number of different number pairs.
Please notice that, (x=5, y=7) and (x=7, y=5) are considered to be the same.

Yoiu can assume that a = c = 1 in all test cases.

Input
The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 3,000 cases.
Each case contains five integers: a, b, c, d, k, 0 < a <= b <= 100,000, 0 < c <= d <= 100,000, 0 <= k <= 100,000, as described above.
Output
For each test case, print the number of choices. Use the format in the example.
Sample Input
2
1 3 1 5 1
1 11014 1 14409 9
Sample Output
Case 1: 9
Case 2: 736427


        
  
Hint
For the first sample input, all the 9 pairs of numbers are (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 5), (3, 4), (3, 5).
        
 


本题和上题不同的是,需要去重

(1,2)和(2,1)是一样的



#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

#define MAXN 1000000
#define LL long long
bool check[MAXN+10];
LL primer[MAXN+10];
LL mu[MAXN+10];
LL sum[MAXN+10];

void Moblus()
{
    memset(check,0,sizeof(check));
    mu[1]=1;
    int tot=0;
    for(int i=2;i<=MAXN;i++){
        if(!check[i]){
            primer[tot++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<tot&&i*primer[j]<=MAXN;j++){
            check[i*primer[j]]=true;
            if(i%primer[j]==0){
                mu[i*primer[j]]=0;
                break;
            }
            mu[i*primer[j]]=-mu[i];
        }
    }
    sum[0]=0;
    for(int i=1;i<=MAXN;i++)
        sum[i]=sum[i-1]+mu[i];
}

LL solve(int n,int m)
{
    LL ans=0;
    if(n>m) swap(n,m);
    for(int i=1,last;i<=n;i=last+1){
        last=min(n/(n/i),m/(m/i));
        ans+=(LL)(sum[last]-sum[i-1])*((LL)(n/i)*(m/i));
    }
    return ans;
}

int main()
{
    int T;
    Moblus();
    int a,b,c,d,k,cases=1;
   // freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        printf("Case %d: ",cases++);
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if(k==0){
            puts("0");
            continue;
        }
        LL ans=solve(b/k,d/k)-solve((a-1)/k,d/k)
        -solve(b/k,(c-1)/k)+solve((a-1)/k,(c-1)/k);
        int x=max(a,c);
        int y=min(b,d);
        if(y>=x){
            ans-=solve(y/k,y/k)/2;
            ans+=solve(x/k,x/k)/2;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值