HDU1695 GCD【容斥原理】【欧拉函数】

数论 同时被 3 个专栏收录
76 篇文章 1 订阅
46 篇文章 0 订阅
153 篇文章 1 订阅

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1695

 

题目大意:

给你5个整数a、b、c、d、k,在区间[a,b]中选一个数x,在区间[c,d]中选一个数y,使得x和y的

公约数为k,即gcd(x,y) = k。现在问题来了:这样的整数对共有多少对。

 

思路:

题目假定a = c = 1,那么区间就变为了[1,b]和[1,d]。求gcd(x,y) = k,其实可以将区间端点除以

k,得到[1,b/k]和[1,d/k]。问题就变为了[1,b/k]和[1,d/k]中共有多少互素的整数对。

由于b可能大于d,为了方便,我们令d > b,不满足则交换d和b。

我们可以固定较小的区间[1,b/k], 用i遍历另一个区间[1,d/k]。

当i <= b/k时,可以很容易看出,求i与[1,b/k]中互质的数的对数就是求i的欧拉函数,累加起来就是结果。

当i > b/k时,就变为了和HDU4135、HDU2841类似的问题,用容斥定理来求。

 

AC代码:

 

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define LL __int64
using namespace std;

LL Q[100010],factor[110000],num;

LL Prime[100010],phi[100010];
bool UnPrime[100010];

void Euler()
{
    int k = 0;
    phi[1] = 1;
    for(int i = 2; i <= 100000; ++i)
    {
        if(!UnPrime[i])
        {
            Prime[k++] = i;
            phi[i] = i-1;
        }
        for(int j = 0; j < k && Prime[j]*i <= 100000; ++j)
        {
            UnPrime[Prime[j]*i] = true;
            if(i % Prime[j] != 0)
            {
                phi[Prime[j]*i] = phi[i]*(Prime[j]-1);
            }
            else
            {
                phi[Prime[j]*i] = phi[i]*Prime[j];
                break;
            }
        }
    }
    for(int i = 2; i <= 100000; ++i)
        phi[i] += phi[i-1];
}

void Divid(LL n)
{
    num = 0;
    for(LL i = 2; i*i <= n; ++i)
    {
        if(n%i==0)
        {
            while(n%i==0)
            {
                n /= i;
            }
            factor[num++] = i;
        }
    }
    if(n != 1)
        factor[num++] = n;
}

LL solve(LL n)  //»¥³â¶¨Àí
{
    LL k,t,ans;
    t = ans = 0;
    Q[t++] = -1;
    for(LL i = 0; i < num; ++i)
    {
        k = t;
        for(LL j = 0; j < k; ++j)
            Q[t++] = -1*Q[j]*factor[i];
    }
    for(LL i = 1; i < t; ++i)
        ans += n/Q[i];
    return ans;
}

int main()
{
    int T,kase = 0;
    Euler();
    scanf("%d",&T);
    while(T--)
    {
        LL a,b,c,d,k,ans;
        scanf("%I64d %I64d %I64d %I64d %I64d",&a,&b,&c,&d,&k);
        if(k == 0)
        {
            printf("Case %d: 0\n",++kase);
            continue;
        }
        if(b > d)
            swap(b,d);
        b /= k;
        d /= k;
        ans = phi[b];
        for(LL i = b+1; i <= d; ++i)
        {
            Divid(i);
            ans += (b - solve(b));
        }

        printf("Case %d: %I64d\n",++kase,ans);
    }

    return 0;
}

 

 

 

 

 

  • 0
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值