Co-prime HDU - 4135 (位运算,队列数组两种方法详解)

Co-prime

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


 

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
 

Recommend
lcy   |   We have carefully selected several similar problems for you:  1796 1434 3460 1502 4136 
 

题意分析:

给定一个数 N,您需要计算 A 和 B 之间与 N互质的整数的数量。

互质:如果两个整数除了 1 之外没有共同的正除数,则称它们互质或互质

解题思路:

一开始还准备筛法,后来发现真是关公门前耍大刀......蠢!!!

题目给的A,B范围1到1e15,很显然你硬筛是行不通的。

换种思路,如果我们把A到B之间和N都不互质的数值求出来,那么B-A+1-这个数值就是答案。

我们知道一个数的质因数的倍数都不与它本身互质

例如:

 A为1,B为20,N为6.

那么N的质因子通过质因子分解:2,3;

显然(1,20)中所有为因子的倍数都不互质,2的倍数(2,4,6,8,10,12,14,18,16)= 20 / 2 = 10; 3的倍数(3,6,9,12,15,18)= 20 / 3; 

      

很明显不互质的答案并不是直接相加,因为里面有重复的,这时候就用到了我们的容斥定理,也就是n / 2 + n / 3 - n / ( 2 * 3 ),

我们发现当除数是奇数的时候加,当除数是偶数的时候减,将重复的排除掉,再次相加就得到了所有不互斥的答案,我们离成功只差一步。

但是我们该怎样完全不重不漏的找到所有质因子的倍数呢?

其实从上面推导出的公式我们可以看出来,要找所有因子的倍数,就可以将已经得到的质因子组合起来,无论两两还是三三四四组合,他们的倍数都将是质因子的倍数

现在问题有转换为了怎么求出已有的质因子所有的组合数?

这里给出有两种方法:

1. 位运算

        我们将所有计算倍数的情况写出来

        001(2的倍数),010(3的倍数),011(2*3,6的倍数),100(5的倍数),101(5*2,10的倍数),110(5*3,15的倍数),111(2*3*5,30的倍数)

        很容易地发现,二进制表示中1的个数为奇数的情况要加,偶数情况要减。        

2. 队列数组

        这个其实就是硬算,将所有情况都硬算一遍,让所有的质因子每次都和组合出来的新数字再组合一遍。个人感觉队列数组结合代码是比较好理解的。

下面可以根据代码再理解一下:

// 1. 位运算
#include <iostream>
#include <cstring>
typedef long long ll;
using namespace std;

ll cnt, primes[10100], que[100005];
void DecPrimes(ll x)// 分解质因数 
{
	cnt = 0;
	for(int i = 2; i * i <= x; i ++)
	{
		if(x % i == 0) 
		{
			primes[cnt ++] = i;
			while(x % i == 0) x /= i;// 避免出现质因数是另一个质因数得倍数 
		}
	}
	if(x != 1) primes[cnt ++] = x; 
}

ll Class(ll m)
{
	ll res = 0, ans = 0;
	for(int i = 1; i < ll(1 << cnt); i ++) 
	{
		res = 1;
		ll flag = 0;
		for(ll j = 0; j < cnt; j ++)// 遍历所有位置,找1 
		{
			if(i & (ll(1 << j)))// 出现因子 因为j是某个位置,所以为1 << j 
			{
				flag ++;
				res *= primes[j];
			}
		 } 
		if(flag & 1)
			ans += m / res;
		else
			ans -= m / res;
	}
	
	return ans; 
}

int main()
{
	int t, C = 1;
	cin >> t;
	while(t --)
	{
		memset(primes, 0, sizeof primes);
		ll a, b, n;
		cin >> a >> b >> n;
		DecPrimes(n);
		printf("Case #%d: %lld\n",C ++, b - Class(b) - (a - 1 - Class(a - 1)));
	}
	
} 

 

// 2.队列运算 
#include <iostream>
#include <cstring>
#define ll long long
using namespace std;

ll cnt, primes[10100], que[100005];
void DecPrimes(ll x)// 分解质因数 
{
	cnt = 0;
	for(int i = 2; i * i <= x; i ++)
	{
		if(x % i == 0) 
		{
			primes[cnt ++] = i;
			while(x % i == 0) x /= i;// 避免出现质因数是另一个质因数得倍数 
		}
	}
	if(x != 1) primes[cnt ++] = x; 
}

ll Class(ll m)
{
	ll k, front = 0, sum = 0;
	que[front ++] = -1;// que[1]一开始是为 -1,因为要符合 “奇加偶减”
	for(int i = 0; i < cnt; i ++) 
	{
		k = front;// 记录上次有多少个组合数 
		for(ll j = 0; j < k; j ++)// 这个质因数和前面的组合全部 ×一边 
			que[front ++] = que[j] * primes[i] * -1;// 容斥定理“奇加偶减” 
	}
	
	for(ll i = 1; i < front; i ++)// 得到所有因子 
		sum += m / que[i]; 
	
	return sum; 
}

int main()
{
	int t, C = 1;
	cin >> t;
	while(t --)
	{
		ll a, b, n;
		cin >> a >> b >> n;
		DecPrimes(n);
		printf("Case #%d: %lld\n",C ++, b - Class(b) - ((a - 1) - Class(a - 1)));
	}
	
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值