[CQOI2015]选数
Link
Solution
可以先把 \(k\) 的限制去了,区间变为 \([\lceil\frac{L}{k}\rceil,\lfloor \frac{H}{k} \rfloor]\),记为 \([l,r]\) 。那么只需要使 gcd 为一。
考虑一波反演,定义 \(F(n)\) 表示 gcd 是 \(n\) 的倍数的方案数,\(f(n)\) 表示 gcd 恰好是 \(n\) 的方案数,那么
\[F(n)=\Big( \lfloor \frac{r}{n} \rfloor - \lfloor \frac{l-1}{n} \rfloor \Big )^N=\sum_{n|d} f(d)
\]
则有
\[f(n)=\sum_{d|n} \mu(\frac{d}{n})F(d)
\]
答案就是
\[f(1)=\sum_{i=1}^{r} \mu(i)\Big( \lfloor \frac{r}{i} \rfloor - \lfloor \frac{l-1}{i} \rfloor \Big )^N
\]
套一个整数分块,然后用杜教筛求 \(mu\) 的区间和就可以了。注意整数分块的时候,\(F\) 的值和 \(\frac{r}{i}\) 和 \(\frac{l-1}{i}\) 都有关。
#include<stdio.h>
#include<unordered_map>
#include<cassert>
using namespace std;
#define ll long long
const int N=5e6+7;
const int Mod=1e9+7;
bool mk[N];
int p[N],cnt=0;
ll L,R,mu[N];
unordered_map<int,int> mp;
ll qpow(ll x,ll y){
ll ret=1;
while(y){
if(y&1) ret=ret*x%Mod;
x=x*x%Mod,y>>=1;
}
return ret;
}
void pre(){
mu[1]=1;
for(int i=2;i<N;i++){
if(!mk[i]){p[++cnt]=i,mu[i]=-1;}
for(int j=1;j<=cnt&&p[j]*i<N;j++){
mk[p[j]*i]=1;
if(i%p[j]==0) break;
mu[i*p[j]]=-mu[i];
}
mu[i]=(mu[i-1]+mu[i]+Mod)%Mod;
}
}
ll solve(ll x){
if(x<N) return mu[x];
if(mp[x]) return mp[x];
ll ret=1;
for(ll l=2,r=0;l<=x;l=r+1){
r=x/(x/l);
ret=(ret-(r-l+1)*solve(x/l)%Mod+Mod)%Mod;
}
return mp[x]=ret;
}
int main(){
ll n,k,H; pre();
scanf("%lld%lld%lld%lld",&n,&k,&L,&H);
L=(L-1)/k+1,R=H/k;
ll ans=0;
for(ll l=1,r=0;l<=R;l=r+1){
if(l>=L) r=R/(R/l);
else r=min(R/(R/l),(L-1)/((L-1)/l));
ans=(ans+qpow(R/l-(L-1)/l,n)*(solve(r)-solve(l-1)+Mod)%Mod)%Mod;
}
printf("%lld",ans);
}