題意:就是讓你求(a,b)區間於n互質的數的個數.
分析:我們可以先轉化下:用(1,b)區間與n互質的數的個數減去(1,a-1)區間與n互質的數的個數,那么現在就轉化成求(1,m)區間於n互質的數的個數,如果要求的是(1,n)區間與n互質的數的個數的話,我們直接求出n的歐拉函數值即可,可是這里是行不通的!我們不妨換一種思路:就是求出(1,m)區間與n不互質的數的個數,假設為num,那么我們的答案就是:m-num!現在的關鍵就是:怎樣用一種最快的方法求出(1,m)區間與n不互質的數的個數?方法實現:我們先求出n的質因子(因為任何一個數都可以分解成若干個質數相乘的),如何盡快地求出n的質因子呢?我們這里又涉及兩個好的算法了!第一個:用於每次只能求出一個數的質因子,適用於題目中給的n的個數不是很多,但是n又特別大的;(http://www.cnblogs.com/jiangjing/archive/2013/06/03/3115399.html)第二個:一次求出1~n的所有數的質因子,適用於題目中給的n個數比較多的,但是n不是很大的。(http://www.cnblogs.com/jiangjing/archive/2013/06/01/3112035.html)本題適用第一個算法!舉一組實例吧:假設m=12,n=30.
第一步:求出n的質因子:2,3,5;
第二步:(1,m)中是n的因子的倍數當然就不互質了(2,4,6,8,10)->n/2 6個,(3,6,9,12)->n/3 4個,(5,10)->n/5 2個。
如果是粗心的同學就把它們全部加起來就是:6+4+2=12個了,那你就大錯特錯了,里面明顯出現了重復的,我們現在要處理的就是如何去掉那些重復的了!
第三步:這里就需要用到容斥原理了,公式就是:n/2+n/3+n/5-n/(2*3)-n/(2*5)-n/(3*5)+n/(2*3*5).
第四步:我們該如何實現呢?我在網上看到有幾種實現方法:dfs(深搜),隊列數組,位運算三種方法都可以!上述公式有一個特點:n除以奇數個數相乘的時候是加,n除以偶數個數相乘的時候是減。我這里就寫下用隊列數組如何實現吧:我們可以把第一個元素設為-1然后具體看代碼如何實現吧!
同種類型的題目:hdu 2841 hdu1695
代碼實現:
#include<iostream> #include<string.h> using namespace std; __int64 a[1000],num; void init(__int64 n)//求一個數的質因子 { __int64 i; num=0; for(i=2;i*i<=n;i++) { if(n%i==0) { a[num++]=i; while(n%i==0) n=n/i; } } if(n>1)//這里要記得 a[num++]=n; } __int64 haha(__int64 m)//用隊列數組實現容斥原理 { __int64 que[10000],i,j,k,t=0,sum=0; que[t++]=-1; for(i=0;i<num;i++) { k=t; for(j=0;j<k;j++) que[t++]=que[j]*a[i]*(-1); } for(i=1;i<t;i++) sum=sum+m/que[i]; return sum; } int main() { __int64 T,x,y,n,i,sum; while(scanf("%I64d",&T)!=EOF) { for(i=1;i<=T;i++) { scanf("%I64d%I64d%I64d",&x,&y,&n); init(n); sum=y-haha(y)-(x-1-haha(x-1)); printf("Case #%I64d: ",i); printf("%I64d\n",sum); } } return 0; }