hdu 1695 莫比乌斯反演

57 篇文章 0 订阅
42 篇文章 0 订阅

GCD

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 10197    Accepted Submission(s): 3848


Problem Description
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).



题意:

给定(a,b)和(c,d)两区间,分别选择一个数字使得gcd(x,y)==k

问:这样的数对有多少对?


题解:

莫比乌斯反演,下面的链接讲解的蛮清晰的

http://wenku.baidu.com/link?url=2rQAVIT4PxnC99LQxtLTK9-1dN3QpWGoZ6ECXFTFGzINi_1PVS3YYC0Ai22rlFPhiz0dXgafMlAcgS2AhQfBt4yNYP5nqqbssxtxj8eqZbi

【a,b】和【c,d/】同时除以k得到【1,b/k】和【1,d/k 】

问题转化为从【1,b/k】和【1,d/k 】分别选择一个数字使得gcd(x,y)==1

莫比乌斯反演的两种形式:




上面的一种形式相对来说比较好求,μ(d)函数就是下面代码中的mu数组


莫比乌斯函数μ(d)定义
编辑
若d=1 那么μ(d)=1
若d=p1p2…pr (r个不同质数,且次数都为一)μ(d)=(-1)r
其余 μ(d)=0

莫比乌斯函数的性质:

性质一(莫比乌斯反演公式):
性质二: μ( n)是积性函数
性质三:设f是算术函数,它的和函数F(x)是积性函数,那么 f 也是积性函数。


F(d)表示 有多少对(x,y)满足 gcd(x,y)== d *k(k为任意正整数)

f(d) 表示有多少对(x,y)满足 gcd(x,y)== d

易求得:

 

对于上叙公式的理解:

假设当 gcd (a,b)== x=2,n=8,m=11

在两个区间【1,8】和【1,11 】中选择2的倍数

第一个区间选择2  第二个区间可以选择  2  4  6  8  10

第一个区间选择3  第二个区间可以选择  2  4  6  8  10

第一个区间选择4  第二个区间可以选择  2  4  6  8  10

第一个区间选择6  第二个区间可以选择  2  4  6  8  10

任意两个数字组合得到的(a,b)计算最大公约数都是x的倍数

这样的数对有(8 / 2)*(11 / 2)==20个


注意:

(1,2)和(2,1)算一种情况,那么我们就要减去多余了的情况,由下图知

(图片借另外一篇bloghttp://blog.csdn.net/lixuepeng_001/article/details/50577932)



   G(b,b)就是多算进去的这些情况

   那么 G(b,d)- G(b,b)/ 2 就是最终结果




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

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

void Moblus()
{
    mu[1]=1;
    int tot=1;
    for(int i=2;i<=MAXN;i++){
        if(!check[i]){
            primer[tot++]=i;
            mu[i]=-1;
        }
        for(int j=1;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];
        }
    }
}

int main()
{
    int T;
    Moblus();
    int cases=1,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);
        if(!k){
            printf("Case %d: 0\n",cases++);
            continue;
        }

        b/=k;
        d/=k;
        if(b>d)
            swap(b,d);
        long long ans1=0,ans2=0;
        for(int i=1;i<=b;i++)
            ans1+=(long long)mu[i]*(b/i)*(d/i);
        for(int i=1;i<=b;i++)
            ans2+=(long long)mu[i]*(b/i)*(b/i);
        printf("Case %d: %I64d\n",cases++,ans1-ans2/2);
    }
    return 0;
}



维护一个前缀和进行优化

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

#define MAXN 100000
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];
}

int main()
{
    int T;
    Moblus();
    int cases=1,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);
        if(!k){
            printf("Case %d: 0\n",cases++);
            break;
        }

        b/=k;
        d/=k;
        if(b>d)
            swap(b,d);

        int last;
        long long ans1=0,ans2=0;
        for(int i=1;i<=b;i=last+1){
             last=min(b/(b/i),d/(d/i));
             ans1+=(long long)(sum[last]-sum[i-1])*(b/i)*(d/i);
        }
        for(int i=1;i<=b;i=last+1){
            last=b/(b/i);
            ans2+=(long long)(sum[last]-sum[i-1])*(b/i)*(b/i);
        }
        printf("Case %d: %I64d\n",cases++,ans1-ans2/2);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值