#include<stdio.h> #include<iostream> #include<string.h> #include<bits/stdc++.h> using namespace std; long long s[1020],k; void prime(long long m)//求一个数的素因子 { long long i; k=0; for(i=2;i*i<=m;i++) { if(m%i==0) { s[k++]=i; while(m%i==0) m/=i; //printf("%d ",m); } } if(m>1) s[k++]=m; //printf("### "); } long long quc(long long m)//队列数组实现容斥原理 { long long p[10020],i,j,t=0,sum=0,z; p[t++]=-1; for(i=0;i<k;i++) { z=t; for(j=0;j<z;j++) { p[t++]=p[j]*s[i]*(-1); } } for(i=1;i<t;i++) sum+=m/p[i]; return sum; } int main() { long long n; scanf("%lld",&n); while(n--) { long long a,b,m,sum; scanf("%lld %lld",&a,&m); long long t = __gcd(a, m); a = a / t; m = m / t; prime(m); sum=(a + m - 1)-quc(a + m - 1)-(a-1)+quc(a-1); printf("%lld ",sum); } }
当r特别大的时候,时间效率会非常低下,那么我们可以考虑容斥定理。 容斥定理的思想:去求解原问题的逆问题−求区间[1,r]中不与n互素的个数。 思路:首先求得n的所有素因子Pi(i=1...k), 通过二进制枚举所有素因子的组合, 如果选取了奇数个素 因子,应该加上对应的个数,而如果是偶数, 则减去即可。 例如6既能2整除,也能被2∗3整除,所以应该剔除重复计数的部分(使用容斥定理)。 假设当前得到的素数组合是k, 那么⌊rk⌋就是[1,r]中能被k整除的数的个数。 详细见容斥定理 详细代码如下 int solve(int r, int n) { vector<int>p; p.clear(); for(int i = 2; i*i <= n; ++i) { if(n % i == 0) { p.push_back(i); while(n % i == 0) n /= i; } } if(n > 1) p.push_back(n); //可能n也是素数 int sum = 0; for(int msk = 1; msk < (1<<p.size()); ++msk) { int mult = 1, bits = 0; for(int i = 0; i < p.size(); ++i) { if(msk & (1<<i)) { //选择了第i个素数因子 bits++; mult *= p[i]; } } int cur = r / mult; if(bits & 1) sum += cur; else sum -= cur; } return r - sum; }