HDU4135 Co-prime(容斥原理)

Problem Description

Given a number N, you are asked to count the number of integers
between A and B inclusive which are relatively prime to N. Two
integers are said to be co-prime or relatively prime if they have no
common positive divisors other than 1 or, equivalently, if their
greatest common divisor is 1. The number 1 is relatively prime to
every integer.

Input

The first line on input contains T (0 < T <= 100) the number of test
cases, each of the next T lines contains three integers A, B, N where
(1 <= A <= B <= 1015) and (1 <=N <= 109).

Output

For each test case, print the number of integers between A and B
inclusive which are relatively prime to N. Follow the output format
below.

Sample Input

2
1 10 2
3 15 5

Sample Output

Case #1: 5
Case #2: 10

思路

关于容斥原理的大体介绍: 容斥原理

题目给出了一个区间[a,b]和一个数n,让你求出a~b中与n互质的数的个数,首先暴力肯定是会TLE的,我们考虑一下容斥原理的做法。

要求出从 a a b中与 n n 互质的数的个数,我们只需要求出从1~bn互质的数的个数减去1~a中与 n n 互质的数的个数,那么问题就转化成了:

已知两个数n,m,求出从1到m中与n互质的数的个数

我们假设n是100,m是20,那么如何求出从1到20中,与100互质的数的个数呢?

首先对100进行质因数分解,结果肯定是2,5,那么就从这20个数中减去因数存在2的,再减去因数存在5的,再加上因数存在10的(10是2和5的倍数,被计算了两次),也就是:

ans=2020/220/5+20/(25)=20104+2=8

这就是利用了容斥原理的思想。

所以解决这个题,只需要进行质因数分解,和容斥原理

代码

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
#define inf 1000000
#define mem(a,b) memset(a,b,sizeof(a))
ll a[50],b[1010];//a保存n的质因子,a[0]表示质因子个数
void div(ll n)//分解质因数
{
    ll j=0;
    for(ll i=2; i*i<=n; i++)
        if(n%i==0)
        {
            while(n%i==0)
                n/=i;
            a[++j]=i;
        }
    if(n>1) a[++j]=n;
    a[0]=j;
}
ll get_cnt(ll mid)//1--mid之间与n互质的数有多少个
{
    ll g=0,sum=mid,t;
    b[++g]=1;
    for(ll i=1; i<=a[0]; i++)
    {
        t=g;
        for(ll j=1; j<=g; j++)
        {
            b[++t]=b[j]*a[i]*-1;
            sum+=mid/b[t];
        }
        g=t;
    }
    return sum;
}


int main()
{
    ll t,q=1,a,b,n;
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld%lld%lld",&a,&b,&n);
        div(n);
        printf("Case #%lld: %lld\n",q++,get_cnt(b)-get_cnt(a-1));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值