<题目链接>
题目大意:
给定区间[A,B](1 <= A <= B <= 10 15)和N(1 <=N <= 10 9),求出该区间中与N互质的数的个数。
解题分析:
将求区间[A,B]与N互质的数转化成求[1,B] 区间与N互质的个数 - [1,A-1]中与N互质的个数。同时,因为直接求区间内与N互质的数不好求,我们从反面入手,求出与N不互质的数,借鉴埃筛的思想,我们先求出N的所有质因子,然后将这些质因子在区间内倍数的个数全部求出(即与N不互质的数),再用区间的总数减去这些不互质数的个数即可。但是,由于在求不互质的数的时候,存在重复的计算,所以我们利用容斥对重复计算的数进行处理。容斥处理有多重表现形式,DFS、队列、位运算均可进行容斥处理。
得到一个数的所有质因子:
for(ll i=2;i*i<=m;i++) if( m%i==0){ //得到m的所有的素因子 vec.push_back(i); while(m%i==0)m/=i; } if(m>1)vec.push_back(m);
位运算:
1 #include <cstdio> 2 #include <cstring> 3 #include <vector> 4 #include <algorithm> 5 using namespace std; 6 7 typedef long long ll; 8 vector<ll>vec; 9 ll a,b,n; 10 11 ll solve(ll x,ll m){ //[1,x]区间内与m4互质的数的个数 12 vec.clear(); 13 for(ll i=2;i*i<=m;i++) if( m%i==0){ //得到m的所有的素因子 14 vec.push_back(i); 15 while(m%i==0)m/=i; 16 } 17 if(m>1)vec.push_back(m); 18 ll sum=0,val,cnt; 19 for(ll i=1;i<(1<<vec.size());i++){ //枚举所有素因子的乘积组合,用二进制表示哪几个因子被用到 20 val=1,cnt=0; 21 for(ll j=0;j<vec.size();j++){ 22 if(i & (1<<j)) { //因为vec里全为质数,所以它们进行组合的时候,直接相乘就行,而不用求lcm 23 val*=vec[j],cnt++; //cnt表示当前相乘因子的个数,用于后面容斥时进行加减的判断 24 } 25 } 26 //容斥,奇加偶减 27 if(cnt & 1)sum+=x/val; // x/tval为[1,x]内为tval的倍数的数的个数 28 else sum-=x/val; 29 } 30 return x-sum; //[1,x]的总数减去1~X中各素数倍数的总数 31 } 32 33 int main(){ 34 int T,ncase=0;scanf("%d",&T);while(T--){ 35 scanf("%lld%lld%lld",&a,&b,&n); 36 ll ans=solve(b,n)-solve(a-1,n); 37 printf("Case #%d: %lld ",++ncase,ans); 38 } 39 }
2019-02-09