題目
我們知道,從區間[L,H](L和H為整數)中選取N個整數,總共有(H-L+1)^N種方案。小z很好奇這樣選出的數的最大公約數的規律,他決定對每種方案選出的N個整數都求一次最大公約數,以便進一步研究。然而他很快發現工作量太大了,於是向你尋求幫助。你的任務很簡單,小z會告訴你一個整數K,你需要回答他最大公約數剛好為K的選取方案有多少個。由於方案數較大,你只需要輸出其除以1000000007的餘數即可。
題解
按照求區間gcd的套路,我們對所有數除以k.對應的將l和r也除以k
這樣目標就是在$(l,r]$內找n個gcd恰好為1的數
我們設 $tot[i]$ 表示gcd恰好為i的方案數,$tot2[i]$ 表示gcd為i或i的倍數方案數。
仿照之前的套路,設$x=l/i,y=r/i$ 則在$(x.y]$中任意取n個數都是滿足要求的
則$tot2[i]=(y-x)^n$
則$tot[i]=tot2[i]-(y-x)-tot[i*2]-tot[i*3]...$
因為gcd最大隻能到$r-l$(再大r就等於l了,這個區間就沒東西了)
所以i只用列舉到$r-l$
減去$y-x$是因為如果選n個同樣的數a,那麼他們的gcd就是a,而a是1e9級別的,在上式中無法排除,所以我們在將上式減去。
但如果k在$(l,r]$內,則全部選k也是合法的答案,要加回一
最後是時間複雜度,設$n=r-l$
上述過程複雜度為 $n/1+n/2+n/3+n/4+..+n/n$
這是一個調和級數,複雜度是$O(nlogn)$
證明:
上式可以放大成 $n/1+n/2+n/2+n/4+n/4+n/4+n/4+..+n/n$
等於$n/1+n/2*2+n/4*4+n/8*8+n/4+...$
也就是 $nlogn$
上面用的都是前開區間,所以要將題目輸入的l減一
程式碼
#include <iostream> #include <cstdio> using namespace std; #define N 100010 #define int long long #define mod 1000000007 int tot[N];//gcd剛好是i的情況數(在除了k的情況下) int qpow(int a,int b) { int ans=1; while(b) { if(b&1) ans*=a,ans%=mod; a*=a; a%=mod; b>>=1; } return ans; } signed main() { int n,k,l,r; cin>>n>>k>>l>>r; bool tag=(k>=l&&k<=r); //如果gcd在區間內,那麼都選gcd這一位也是合法的 l--; l/=k,r/=k; int len=r-l; for(int i=len;i;i--) { int x=l/i,y=r/i; if(x>=y) continue; tot[i]=qpow(y-x,n)-(y-x);//減去都選同一位的情況 for(int j=2;i*j<=len;j++) tot[i]+=mod-tot[i*j],tot[i]%=mod; //減去選擇的方案gcd為i的倍數的情況 ,使gcd恰好為i } cout<<tot[1]+tag; }