//容斥原理的实现
//1.队列数组
//2.dfs
//3.二进制表示
//队列数组是枚举所有情况,可能有时候空间上存不下?dfs的优点是用时间换空间
ps:
1.int fac[15]
//13! > 1e8,所以质因子的个数小于15 //long long 的话21! > ull,25肯定够
2.二进制
注意空集不要算
//3.如果要求[1..n]中与m不互质的数的个数,即二进制枚举子集,求子集所有数字的lcm,n / lcm即可
1.队列数组
m = p1 ^ x1 * p2 ^ x2 * ... * pk ^ xk,求小于等于m的,整除p1或p1或...pk的数的个数
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int maxn =1e5 +5;
int q[maxn],fac[15],num;//队列数组q,fac记录m的质因子,num记录质因子个数
void divide(int m)
{
num = 0;
for (int i =2; i <=sqrt(0.5 + m); i ++) {
if(m % i == 0){
fac[num ++] = i;
while(m % i == 0) m /= i;
}
if(m == 1)break;
}
if(m != 1)fac[num ++] = m;
}
int solve(int m)//小于等于m中,和m不互质的数的个数
{
int sz = 0;
q[sz ++] = -1;
for (int i =0; i <num; i ++) {
int k = sz;
for (int j =0; j < k; j ++) {
q[sz ++] = -1 *fac[i] *q[j];
}
}
//计算A并B并C...个数
int ans = 0;
for (int i =1; i < sz; i ++) {
ans += m / q[i]; //m = 2 * 3 * 5,n / q[i]分别等于15,10,6,-5,-3,-2,1,即各个块的大小,可以用他们的个数求别的
}
return ans;
}
int main()
{
int m;
while (scanf("%d",&m) !=EOF) {
divide(m);
int ans = solve(m);
printf("%d\n",ans);
}
return 0;
}
//2.dfs
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
int fac[15],num;
int m;
void divide(int m)
{
num = 0;
for (int i =2; i <=sqrt(0.5 + m); i ++) {
if(m % i == 0){
fac[num ++] = i;
while(m % i == 0) m /= i;
}
if(m == 1)break;
}
if(m != 1)fac[num ++] = m;
}
//其实本质上就是枚举所有情况,和二进制枚举没啥区别
//pos为当前质因子的位置,cnt为当前公共因子的个数,搜索含有c个公共因子的数的个数
int p[15];
int tmp = 0;
void dfs(int pos,int cnt,int c)
{
if(cnt == c){
int x = m;
for (int i =1; i <= c; i ++) {
x /= p[i];//计算个数
}
tmp += x;
return;
}
for (int i = pos; i <num; i ++) {
p[cnt + 1] =fac[i];//记录这一次枚举了谁,放在p中
dfs(i + 1 , cnt +1, c);
}
}
int main()
{
while (scanf("%d",&m) !=EOF) {
divide(m);
ll ans = 0;
for (int i =1; i <=num; i ++) {
tmp = 0;
dfs(0,0, i);
if(i & 1) ans += tmp;
else ans -= tmp;
}
printf("%lld\n",ans);
}
return 0;
}
//3.二进制
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
int fac[15],num;
void divide(int m)
{
num = 0;
for (int i =2; i <=sqrt(0.5 + m); i ++) {
if(m % i == 0){
fac[num ++] = i;
while(m % i == 0) m /= i;
}
if(m == 1)break;
}
if(m != 1)fac[num ++] = m;
}
ll solve(int m)
{
ll ans = 0;
for (int i =1; i < (1 <<num); i ++) {//空集不算,否则会多减一个m
int t = i,x = m,cnt = 0;
for (int j =0; j <num; j ++) {
if(t & 1) {x /= fac[j];cnt ++;}
t >>= 1;
if(t == 0) break;
}
if(cnt & 1) ans += x;
else ans -= x;
}
return ans;
}
int main()
{
int m;
while (scanf("%d",&m) !=EOF) {
divide(m);
ll ans = solve(m);
printf("%lld\n",ans);
}
return 0;
}