HDU 4135 Co-prime

题面:

传送门

Co-prime

Input file: standard input
Output file: standard output
Time limit: 1 second
Memory limit: 256 megabytes
 
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.
 
Example
Input
2
1 10 2
3 15 5
Output
Case #1: 5
Case #2: 10
 

题目描述:

计算在区间[A, B]内与整数N互质的数的个数。
 

题目分析:

这题是容斥定理裸体(也不算裸吧😂),直接去求与N互质的数的个数,用一般的做法肯定会超时,特殊的做法又想不到,所以只好用间接法:求区间[A, B]内与N不互质的数的个数。区间[A, B]内与N不互质的数可以转化为:(0, B]内与N不互质的数的个数,减去(0, A-1]内与N不互质的数的个数。这时问题就变成了:求(0, x]内与N不互质的数的个数。我们思考一下与N不互质的数的特点:不互质的数是N的质因子的倍数。所以我们可以先进行质因子分解。得到质因子后,我们发现:只有一个质因子时,要算一个质因子的倍数的数的个数很容易,比如质因子为2时,(0, 10]内与N不互质的数的个数就是10/2:
当有两个质因子时,要分别算两个质因子的倍数的个数,而且还要减去公共部分,这时就稍微有点麻烦。比如质因子为2和3时,(0, 10]内与N不互质的数的个数就是10/2+10/3-10/(2x3):
当有三个,四个,五个质因子时怎么办?这时,我们就要用到容斥定理:当我们选奇数个质因子时,就是“加”;选偶数个质因子时,就是“减"。比如上面得例子:当质因子为2和3时,选一个(奇数个)质因子:10/2+10/3;选两个(偶数个)质因子:-10/6;把这些数加起来就是结果了。有多个质因子是,我们要保存它们选与不选的状态,那么怎样保存呢?假设我们质因子分解出来是2,3,5,那么,我们用0表示不选,1表示选:
比如,我们要选质因子2和3就可以这样:
我们看看这个“110”,其实是不是可以看成一个二进制数?只要从1遍历到111(2),也就是十进制的2^3-1,然后一位一位地取出来,就可以获得了所有状态。这种方法叫做状态压缩。所有状态图:

我们利用了二进制就很巧妙的把所有的状态用数存了下来。
 
AC代码:
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 long long a, b, n;
 6 int cnt;
 7 long long prime[35];
 8 
 9 long long solve(long long x){   //计算(0, x]内与N不互质的数的数量
10     long long all = (1<<cnt)-1;  //所有选取的可能
11     long long sum = 0;
12 
13     for(int i = 1; i <= all; i++){
14 
15         int t = i;      //取出状态
16         int mul = 1;
17         int flag = 0;   //奇加偶减标记
18 
19         int k = 0;
20         while(t){
21             if(t & 1) {     //判断当前二进制位是否为1
22                 mul *= prime[k];
23                 flag = !flag;
24             }
25             t >>= 1;   
26             k++;
27         }
28 
29         //奇加偶减
30         if(flag) sum += x/mul;   //计算1-x是mul的倍数的个数
31         else sum -= x/mul;
32     }
33 
34     return sum;
35 }
36 
37 void break_up(){
38     cnt = 0;
39     memset(prime, 0, sizeof(prime));
40 
41     for(long long i = 2; i*i <= n; i++){
42         if(n % i == 0){
43             while(n % i == 0) n /= i;
44             prime[cnt++] = i;   //存质因子
45         }
46     }
47     
48     if(n != 1) prime[cnt++] = n;  //存最后一个质因子
49 }
50 
51 int main(){
52     int t;
53     cin >> t;
54     int kase = 1;
55     while(t--){
56         cin >> a >> b >> n;
57 
58         break_up();    //拆分质因子
59         long long temp = solve(b) - solve(a-1); 
60 
61         printf("Case #%d: %lld\n", kase, b-a+1-temp);  //[A, B]区间里面总共的数减去区间内与N不互质的数
62         kase++;
63     }
64     return 0;
65 }

 

 
posted @ 2019-03-08 21:21  MrEdge  阅读(139)  评论(0编辑  收藏  举报