HDU 1695 GCD 数论

题目地址:  http://acm.hdu.edu.cn/showproblem.php?pid=1695

 

 

题目大意:

给你a, b, c, d, k; 找出这样的一队 x, y, 使得 gcd(x , y) = k, 并且x ∈[1, b], y ∈ [1, d], 问有多少对符合要求的(x, y)。

------------------------------------------------------------------------------

 

思路: gcd(x, y) == k 说明x,y都能被k整除, 但是能被k整除的未必gcd=k  , 必须还要满足互质关系. 问题就转化为了求1~a/k 和 1~b/k间互质对数的问题可以把a设置为小的那个数, 那么以y>x来保持唯一性(题目要求, 比如[1,3] =[3,1] )

接下来份两种情况:

1. y <= a , 那么对数就是 1~a的欧拉函数的累计和(容易想到)

2. y >= a , 这个时候欧拉函数不能用了,怎么做?  可以用容斥原理,把y与1~a互质对数问题转换为y的质数因子在1~a范围内能整除的个数(质数分解和容斥关系),dfs一下即可。

 

代码如下;

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <queue>
using namespace std;

/*
freopen("input.txt",  "r", stdin);  //读数据
freopen("output.txt", "w", stdout); //注释掉此句则输出到控制台
*/

const int N = 100005;
typedef long long LL;

LL prime[N];
LL phi[N];
bool is_prime[N];
vector<LL> link[N];

void get_phi()  //筛法欧拉函数
{
    LL i, j, k = 0;
    phi[1] = 1;
    for(i = 2; i < N; i++)
    {
        if(is_prime[i] == false)
        {
            prime[k++] = i;
            phi[i] = i-1;
        }
        for(j = 0; j<k && i*prime[j]<N; j++)
        {
            is_prime[ i*prime[j] ] = true;
            if(i%prime[j] == 0)
            {
                phi[ i*prime[j] ] = phi[i] * prime[j];
                break;
            }
            else
            {
                phi[ i*prime[j] ] = phi[i] * (prime[j]-1);
            }
        }
    }
}

void init()     //求每一个数的质因数,vector储存
{
    LL i, j, k;
    for(i = 1; i < N; i++)
    {
        k = i;
        for(j = 0; prime[j]*prime[j] <= k; j++)
        {
            if(k%prime[j] == 0)
            {
                link[i].push_back(prime[j]);
                while(k%prime[j] == 0)
                    k /= prime[j];
            }
            if(k == 1) break;
        }
        if(k > 1) link[i].push_back(k);
    }
}

LL dfs(LL a, LL b, LL cur)  //容斥原理计算
{
    LL i, k, res = 0;
    for(i = a; i < link[cur].size(); i++)
    {
        k = b/link[cur][i];
        res += k - dfs(i+1, k, cur);
    }
    return res;
}

int main()
{
    LL i, a, b, c, d, k, sum, t, zz = 1;
    get_phi();
    init();
    scanf("%I64d", &t);
    while(t--)
    {
        scanf("%I64d %I64d %I64d %I64d %I64d", &a, &b, &c, &d, &k);
        if(k == 0 || k > b || k > d)
        {
            printf("Case %I64d: 0\n", zz++);
            continue;
        }
        if(b > d) swap(b, d);
        b /= k;
        d /= k;
        sum = 0;
        for(i = 1; i <= b; i++)
        {
            sum += phi[i];
        }
        for(i = b+1; i <= d; i++)
        {
            sum += b - dfs(0, b, i);
        }
        printf("Case %I64d: %I64d\n", zz++, sum);
    }

    return 520;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值