[bzoj3930] [CQOI2015]選數

linzhuohang發表於2020-08-22

題目

我們知道,從區間[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;
}

  

相關文章