HDU 4135(容斥原理)

Co-prime

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8792    Accepted Submission(s): 3474


 

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

Hint

In the first test case, the five integers in range [1,10] which are relatively prime to 2 are {1,3,5,7,9}.

 

 

Source

The Third Lebanese Collegiate Programming Contest

 

 

Recommend

lcy

 

原地址在这

题意:

就是给你一个区间,再给你一个数,让你求在这个区间里的与这个数互质的数的个数。

题解:

这个题,其实一开始想到暴力呗hhhhhh太聪明了,其实菜成傻子。。。。一看数据范围,真是好玩,居然区间到了1e15,还有那个数也是到了1e9,还算好,hhhh开玩笑,那么大不是劝退吗,好吧,我被劝退了开玩笑别介意。既然那么大,该怎么办呢?老段讲解,反着想,嗯,很对,反着想,你将其变成求所有数减去不互质的数的个数就是答案了,然后通过将其分解质因数,通过对质因数的倍数进行枚举,来快速枚举完这个区间,但是可以预见i减就会把共同的倍数给减多了,那就加回来呗,所以奇加偶减给容斥一下就好了。(特别感谢这里老段,在这里学到了新模板(大整数质因数分解))

代码:

 

#include <bits/stdc++.h>
#define ll long long
using namespace std ;

const int MAXN = 1000005 ;

int64_t mulEx(int64_t a , int64_t b , int64_t Mod) {///logn快速乘
    if(!a) return 0 ;
    int64_t ans(0) ;
    while(b)
    {
		if(b & 1) ans = (ans + a) % Mod;
		a <<= 1 ;
		a %= Mod ;
		b >>= 1 ;
    }
    return ans ;
}

int64_t powEx(int64_t base , int64_t n , int64_t Mod)
{///快速幂
    int64_t ans(1) ;
    while(n)
    {
        if(n & 1) ans = mulEx(ans , base , Mod) ;
        base = mulEx(base , base , Mod) ;
        n >>= 1 ;
    }
    return ans ;
}

bool check(int64_t a , int64_t d , int64_t n)
{
    if(n == a) return true ;
    while(~d & 1) d >>= 1 ;
    int64_t t = powEx(a , d , n) ;
    while(d < n - 1 && t != 1 && t != n - 1)
    {
        t = mulEx(t , t , n) ;
        d <<= 1 ;
    }
    return (d & 1) || t == n - 1 ;
}

bool isP(int64_t n)
{ ///判断大数是否是质数
    if(n == 2) return true ;
    if(n < 2 || 0 == (n & 1)) return false ;
    static int p[5] = {2 , 3 , 7 , 61 , 24251} ;
    for(int i = 0 ; i < 5 ; ++ i) if(!check(p[i] , n - 1 , n)) return false ;
    return true ;
}

int64_t gcd(int64_t a , int64_t b)
{
    if(a < 0) return gcd(-a , b) ;
    return b ? gcd(b , a - b * (a / b)) : a ;
}

int64_t Pollard_rho(int64_t n , int64_t c)
{///大数分解质因数
    int64_t i = 1 , k = 2 , x = rand() % n , y = x ;
    while(true)
    {
        x = (mulEx(x , x , n) + c) % n ;
        int64_t d = gcd(y - x , n) ;
        if(d != 1 && d != n) return d ;
        if(y == x) return n ;
        if(++ i == k)
        {
            y = x ;
            k <<= 1 ;
        }
    }
}

int64_t Fac[MAXN] , factCnt ;
///Fac存的是质因子,大小不一定按照顺序,有重复
void factorization(int64_t n)
{
    if(isP(n))
    {
        Fac[factCnt++] = n ;
        return ;
    }
    int64_t p(n) ;
    while(p >= n) p = Pollard_rho(p , rand() % (n - 1) + 1) ;
    factorization(p) ;
    factorization(n / p) ;
}

map<int64_t , int64_t> factMap ;
///遍历map的first表示因子,second表示次数

void getFactor(int64_t x)
{///不用判断是否是质数,但是比较费时间
 /**因此最好先判断一下是否是质数**/
	srand(time(0)) ;
	factCnt = 0 ;
	factMap.clear() ;
	factorization(x) ;
	for(int i = 0; i < factCnt; ++i) ++ factMap[Fac[i]] ;
}

ll p_o_in_ex(ll n)
{
	ll sum = 0 ;
	int num = factMap.size();
	vector<int64_t> v ;
	for(auto &it:factMap){
        v.push_back(it.first) ;
	}
    for(int i = 1 ; i < (1 << num) ; i ++)
	{
		ll mul = 1 , cnt = 0 ;
		for(int j = 0 ; j < num ; j ++)
		{
			if((i >> j) & 1)
			{
				mul *= v[j] ;
				cnt ++ ;
			}
		}
		int flag = (cnt & 1) ? 1 : -1 ;
		sum += flag * (n / mul) ;
	}
	return sum ;
}

int main()
{
	int T , kase = 0 ;
	scanf("%d" , &T) ;
	while(T --)
	{
		ll A , B , N ;
		scanf("%lld%lld%lld" , &A , &B , &N) ;
		if(N == 1)
        {
            printf("Case #%d: %lld\n" , ++ kase , B - A + 1) ;
            continue ;
        }
		getFactor(N) ;
		printf("Case #%d: %lld\n" , ++ kase ,(B - A + 1) - (p_o_in_ex(B) - p_o_in_ex(A - 1))) ;
	}
	return 0 ;
}

菜的不一样,菜出新高度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值