最近学习容斥原理,实现容斥原理大致有三种方法:dfs,队列数组,二进制。
今天主要讲下二进制实现容斥原理:
有一个集合{A1……An},求集合的子集?很显然答案为
也就是2^n个,也就是每一个子集有唯一标志符 i (0<i<2^n,空集除外),也就是说有唯一的二进制表示!
代码看下面的:
1 #include<iostream>
2 #include<stdio.h>
3 #include<algorithm>
4 #include<iomanip>
5 #include<cmath>
6 #include<cstring>
7 #include<vector>
8 #include<stdlib.h>
9 using namespace std;
10 int prime[40000],m;
11 bool f[40000];
12 vector<int>p;//存放质因数
13 //用筛法初始化40000以内的质数,将质数存放在prime数组中,m记录大小
14 int init(){
15 m=0;
16 for(int i=2; i<40000; i++){
17 if (f[i]==0) prime[m++]=i;//质数
18 //筛去合数
19 for (int j=0; j<m&&i*prime[j]<40000; j++){
20 f[i*prime[j]]=1;
21 if (i%prime[j]==0) break;//保证每个数只筛去一次
22 }
23 }
24 }
25 //对n分解质因数
26 void factor(int n){
27 p.clear();
28 for (int i=0; i<m&&prime[i]*prime[i]<=n; i++){
29 if (n%prime[i]==0){
30 p.push_back(prime[i]);
31 n/=prime[i];
32 while (n%prime[i]==0)
33 n/=prime[i];
34 }
35 }
36 if(n>1) p.push_back(n);
37 }
38 //用二进制实现容斥原理,求区间[1,r]内与n互素的数的个数
39 int solve(int r){
40 int sum = 0;
41 //i的范围是1-2^p.size(),空集除外,每一个子集所对应的
42 //二进制都不一样,也就是i
43 for (int i=1; i<(1<<p.size()); ++i){
44 int mult = 1,bits = 0;
45 for (int j=0; j<p.size(); ++j)
46 if (i&(1<<j)){//与i的二进制的第j位比较,看是否为1,是则选中
47 bits++;//计算i中1的个数,也就是质因数的个数
48 mult *= p[j];
49 }
50 int cur = r / mult;
51 if (bits & 1)//若1的个数是奇数则进行加法,否则进行减法
52 sum += cur;
53 else sum -= cur;
54 }
55 return r - sum;//用总的数目-与n不互素的个数
56 }
57 int main(){
58 init();
59 int n,r;
60 while(cin>>n>>r){
61 factor(n);
62 cout<<solve(r)<<endl;
63 }
64 return 0;
65 }