Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5189 Accepted Submission(s): 2070
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
题意:给出A,B,N三个数,(1<=A<=B<=10^15),(1<=N<=10^9),问在[A,B]区间内有多少个数与N互质?
分析:首先要求的是区间[A,B]内与N的互质的数目,首先想到了这道题可以化成【1,B】的互素个数-【1,A-1】的互素个数,然后老师给出的一个例题,poj3904Sky Code中的方法,将找到互素的个数转化为总数减去不互素(即有公因数)的个数。再结合了 任何一个公约数必然有素因子组成,所以对素因子构成的集合采用容斥定理。 先可以将题中的N的所有素因子找到,然后再用容斥定理找出[1,B]和[1,A-1]内与这些因子的不互素(倍数关系)的个数,用总个数减去即为所求。
但容斥定理的实现,这里明显不是DFS,也不能用二进制位来实现(复杂度O(2^cnt*i)cnt是N的素因子个数),不适用于cnt过大的题。这题中的N的范围是[1,10^9],故会时间超限,那么如何写呢?
我在网上别人的博客中看到第三种实现的方法,队列实现容斥定理(复杂度O(cnt*k)显然比二进制位实现的快得多)。
一个比较巧妙的地方是令front=0;que[front]=-1; 这里其实也是因为容斥定理中的“奇加偶减”的性质,将容斥定理的所有组合方式加入到队列中
具体见注释
AC代码:
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=10005;
LL a[N],cnt;
void is_sfactor(LL n)///求n的所有素因子
{
LL i;
cnt=0;
for(i=2; i*i<=n; i++)
{
if(n%i==0)
{
a[cnt++]=i;///数组a储存素因子,cnt为素因子的个数
while(n%i==0)///将素因子i除尽
n/=i;
}
}
if(n!=1)///这里是因为有的n的因子大于sqrt(n),比如14,他的素因子有2,7,
a[cnt++]=n;
}
LL que[10*N];
LL solve(LL m)///容斥定理的队列实现方式
{
LL i,j,k,front=0,sum=0;
que[front++]=-1;
for(i=0; i<cnt; i++)///所有的素因子都加入队列
{
k=front;//记录上一次循环的所有因子的组合方式的个数
for(j=0; j<k; j++)///用这次的因子a[i]与前面的k中组合进行组合
que[front++]=que[j]*a[i]*(-1);/// *(-1) 是因为容斥定理 “奇加偶减”性质
}
for(i=1; i<front; i++)///front为n的素因子的所有可能的组合方式,front=2^cnt-1,这里有效的把二进制位实现的所有种类个数从循环拿到队列中来了,所以这就适用于cnt的个数(也可以说n)较大的情况
sum+=m/que[i];
return sum;///sum为是n的因子的倍数的个数
}
int main()
{
LL T,a,b,n;
scanf("%lld",&T);
for(int i=1; i<=T; i++)
{
scanf("%lld%lld%lld",&a,&b,&n);
is_sfactor(n);///找出n的所有素因子
LL sum=b-solve(b);//[1,b]内与n互质的个数
LL sum2=a-1-solve(a-1);///[1,a-1]内与n互质的个数
LL ans=sum-sum2;//题目所求
printf("Case #%d: %lld\n",i,ans);
}
return 0;
}
下面也贴上用二进位实现的代码段:
LL solve(LL m)
{
int k,num,sum=0;
for(int i=1; i<(1<<cnt); i++)///一共有2^cnt中组合方式
{
k=1,num=0;///k为因数组合的乘积,num则是记录是容斥定理中的奇数项或是偶数项
for(int j=0; j<i; j++)//i个因子的组合
{
if(i&&(1<<j))///实现具体见下面的例子
{
k*=primes[j];///和primes[j]组合后的乘积
num++;
}
}
if(num&1) sum+=m/k;
else sum-=m/k;
}
return sum;
}
例如:hdoj 2588 GCD
题意:给定不超过100组正整数N和M(x<=N<=10^9,1<=M<=N)
求有多少个正整数X满足1<=X<=N且gcd(X,N)>=M。
这里举出这个例子只是为了比较二进制位和队列两种方式实现容斥定理的时间复杂度的比较,并不说明这题必须用容斥定理。
在这题中,老师的pdf讲解中给出了一个例子, 如:N=10000,M=72 ,但为了符合我要比骄的题,就给出一个小一点的数,其实都是一样的,这里为了方便
例如:N=30,M=3
30的素因子2,3,5,但是2<3就不存入数组 存入数组a,a[0]=3,a[1]=5
利用二进制位实现。
cnt=2
1(2^0) 0对应数组a的下标 3
2 (2^1) 1同理 5
3 (2^0|2^1) 0 1 分别对应3 5
这就是他的几种组合3,5,15
答案就是30/3+30/5-30/15=14
回归我要比较的,二进制位和队列实现的区别在前者于一个把因子搭配种类放在了循环里,而后者巧妙的运用容斥定理的性质,把因子的个数放在循环里,搭配的种类数就是队列中的数的个数。
那么就我目前的感觉(做题还不够多),能用队列实现就用队列,这样的时间消耗大大减少了
以上仅是自己的想法,有不对的地方,望能指出,谢谢