1. 程式人生 > >CQOI2015 選數

CQOI2015 選數

粘題目描述:

我們知道,從區間[L,H](L和H為整數)中選取N個整數,總共有(H-L+1)^N種方案。

小z很好奇這樣選出的數的最大公約數的規律,他決定對每種方案選出的N個整數都求一次最大公約數,以便進一步研究。

然而他很快發現工作量太大了,於是向你尋求幫助。

你的任務很簡單,小z會告訴你一個整數K,你需要回答他最大公約數剛好為K的選取方案有多少個。由於方案數較大,你只需要輸出其除以1000000007的餘數即可。

題解:

容斥+遞推。如果我們在區間[l,r]種任取n個不全相同的數時,他們的gcd一定<=r-l+1(貝祖)。

然後就很好搞了,l=(l+k-1)/k,h=h/k。

然後f[ i ]表示合法區間內選n個數不全相同且gcd==i的方案。

容斥之前是x^n-x,然後逆向處理即可。

程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MOD 1000000007
#define N 100050
#define ll long long
int n,k,l,h;
ll f[N];
ll fastpow(ll x,int y)
{
    ll ret = 1ll;
    while(y)
    {
        if(y&1)ret=ret*x%MOD;
        x
=x*x%MOD; y>>=1; } return ret; } int main() { scanf("%d%d%d%d",&n,&k,&l,&h); l=(l+k-1)/k,h=h/k; for(int i=1;i<=(h-l+1);i++) { int x = (h/i)-((l+i-1)/i)+1; f[i]=fastpow(x,n)-x; } for(int i=(h-l+1);i>=1;i--) {
for(int j=2;i*j<=(h-l+1);j++) { f[i]=(f[i]-f[i*j]+MOD)%MOD; } } printf("%lld\n",f[1]+(l==1)); return 0; }