【HDU】4135 - Co-prime(容斥原理)

点击打开题目

Co-prime

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


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 <= 10 15) and (1 <=N <= 10 9).
 

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
 



这道题确实应该好好的写一篇题解了。

如果是求小于 n 且与 n 互质的数的个数,可以用欧拉函数解决,但是这道题a,b的范围可能大于 k ,所以应该这么用容斥原理来做:

求 1~a-1 中与 k 互质的数,再求 1 ~ b 中与 k 互质的数,后者减去前者就是答案。

然后就是求 1 ~ n 中与 k 互质的数有多少,我们可以反着,先求  1 ~ n 中与 k 不互质的数有多少。


这点求法再拉出来细说:先把 k 分解质因数,存在一个数组中。

举个例子,比如 k 的质因子有 2,3,5。那么2、3、5的倍数都不和 k 互质,另外还没有完,可能有重复的地方,比如6,既是2的倍数又是3的倍数,前面用 k/2 + k/3 的时候多减了,这个时候要加上 k / (2*3)。同理,10,15这一类数都应该加上。但是还有类似于30这样的数,它是2,3,5的倍数,减的时候又多减了。

然后我们会发现,出现奇数个数,就用加法,偶数个数用减法。

最后的式子是这样的:k / 2 + k / 3 + k / 5 - k / (2 * 3) - k / (3 * 5) - k / (2 * 5) + k / (2 * 3 * 5)

有点长,看一下容易发现我说的奇偶的规律。


但是要怎么取算这个又出现问题了。

这里提供二进制的方法,个人觉得比较好理解。

设质因数的个数为m。

有一个浮动的数字,从1 ~ m依次递增,它的二进制的每一位表示用了哪些数字,比如5(101),其二进制的第一位和第三位(倒着数)是1,则它表示用了第一个质因数和第三个质因数。就是这个意思,这样就能发现:从1到m遍历一遍,就把所有的可能都包括了。


写的挺累的,有错误谢谢指正,转载说明出处。


下面看一下代码理解一下:

#include <cstdio>
int p[1000000];
int num;
__int64 a,b,k;
void pr(int x)		//求x的质因子
{
	num = 0;
	for (int i = 2 ; i * i <= x ; i++)
	{
		if (x % i == 0)
		{
			p[num++] = i;
			while (x % i == 0)
				x /= i;
		}
	}
	if (x > 1)
		p[num++] = x;
}
__int64 solve(__int64 n)		//1~n中不与k互质的数 
{
	__int64 ans = 0;
	for (__int64 i = 1; i < (__int64)1 << num ; i++)		//其二进制位为1,表示这些质因数被用到 
	{
		int ant = 0;		//用奇数个质因数加,偶数个减
		__int64 t = 1;
		for (int j = 0 ; j < num ; j++)
		{
			if (((__int64)1 << j) & i)
			{
				t *= p[j];
				ant++;
			}
		}
		if (ant & 1)		//奇数加
			ans += n / t;
		else
			ans -= n / t;
	}
	return ans;
}
int main()
{
	int u;
	int Case = 1;
	scanf ("%d",&u);
	while (u--)
	{
		scanf ("%I64d %I64d %I64d",&a,&b,&k);
		pr(k);
		printf ("Case #%d: ",Case++);
		printf ("%I64d\n",b-(a-1)-(solve(b)-solve(a-1)));
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值