hdu 4135

    Co-prime

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

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}.
 

解题思路:这道题目如果去从a遍历到b,那肯定会TLE,所以这里可以用巧妙点的方法避开枚举。之前在《算法竞赛入门经典》里面看到过这样的问题:小于n且与n互为素数的个数。它的原理是利用容斥原理,那么这个题目的解题思路也是利用容斥原理。关键是如何找到a到b这个区间的数,这个地方确实不太容易想,其实可以先算出[1,b]这个区间内的,再算出[1,a-1]这个区间内的,然后两者相减,就能去掉重叠的部分,算出的就是[a,b]这个区间内与n互为素数的个数。首先当然是把n分解质因数,然后将其保存起来。其中容斥原理的算法可以利用dfs去算。
但是不知道为什么一直WA。。。

我的代码:
#include<iostream>
#include<cmath>
#include<vector>
using namespace std;

vector<int> v;
void primeFactor(int n)
{
	int q = sqrt(n+0.5);
	v.clear();
	for(int i = 2; i <= q; i++)
	{
		if(n % i == 0)
		{
			v.push_back(i);
			while(n % i == 0)
				n = n / i;
		}
	}
	if(n > 1) v.push_back(n);
}

__int64 dfs(int cur,int b,int n)
{
	__int64 res = 0;
	if(b == 0) return 0;
	for(int i = cur; i < v.size(); i++)
		res += b / v[i] - dfs(i+1,b/v[i],n);
	return res;
}

int main()
{	
	int t,cas = 1;
	cin>>t;
	while(t--)
	{
		__int64 a,b,n;
		cin>>a>>b>>n;
		primeFactor(n);
		int ans1 = b - dfs(0,b,n);
		int ans2 = a-1 - dfs(0,a-1,n);
		cout<<"Case #"<<cas++<<": "<<ans1-ans2<<endl;
	}
	return 0;
}


补充一下:关于容斥原理的算法又学到一招:队列数组。   我们可以用一个队列(数组也行)存储出现的分母,我们可以令队列的第一个元素为1,让每次出现的m的因子和队列中的元素一个一个相乘再存储到队列中,最后就会发现存储的元素就是我们上面的分母了。现在的问题又变成了我们时候用加什么时候用减,这里我们只需要每次存的时候在再乘一个(-1),就可以得到我们想要的结果了。
以这道题为例:
__int64 cal(__int64 n, __int64 t)  //表示从[1,t]的区间内,有多少个数与n互为素数
{
    int num=0;
    que[num++]=1;     //队列数组,里面存放的是各个倍数的乘积
    for(int i=0; i<vt.size(); i++)   //vt[i]存放的是第i个质因子
    {
        int ep=vt[i];
        int k=num;
        for(int j=0; j<k; j++)
            que[num++]=ep*que[j]*(-1);    //乘以-1,可以保证“奇负偶正”
    }
    __int64 sum=0;
    for(int i=0; i<num; i++)
        sum+=t/que[i];       //  t/que[i]表示在区间[1,t]中间,因子为que[i]的有多少个
    return sum;
}
其实这个方法感觉很像《算法竞赛入门经典》里面的那个欧拉函数的变形式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值