前置技能:
快速冪(霧)
杜教篩
狄利克雷卷積(莫比烏斯反演)
首先把答案表示出來:
這裏先把和都除以,然後就轉化成。
由珂得
調換枚舉順序,先枚舉,得到
然後整除分塊,求一下的前綴和(杜教篩),然後再來個快速冪即可qwq。
代碼
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<map>
#define re register int
#define rl register ll
#define mod 1000000007
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
inline ll fpow(ll b,ll p,ll k) {
ll ans=1;
while(p) {
if(p&1) ans=ans*b%k;
b=b*b%k;
p>>=1;
}
return ans;
}
const int Size=2000005;
int tot,mu[Size],sum[Size],prime[Size];
bool vis[Size];
void getp(int maxn) {
mu[1]=1;
for(re i=2; i<=maxn; i++) {
if(!vis[i]) {
prime[++tot]=i;
mu[i]=-1;
}
for(re j=1; j<=tot; j++) {
int now=i*prime[j];
vis[now]=true;
if(now>maxn || i%prime[j]==0) {
break;
}
mu[now]=-mu[i];
}
}
for(re i=1; i<=maxn; i++) {
sum[i]=sum[i-1]+mu[i];
}
}
ll n,k,l,r;
//ll val[Size];
map<int,ll> val;
ll Sum(int x) {
if(x<=1000000) return sum[x];
int t=r/x,lst;
if(val[x]) return val[x];
ll ans=0;
for(re i=2; i<=x; i=lst+1) {
lst=x/(x/i);
ans=(ans+(ll)(lst-i+1)*Sum(x/i))%mod;
}
// vis[t]=true;
return val[x]=(1ll-ans+mod)%mod;
}
int main() {
// freopen("WA.txt","w",stdout);
getp(1000000);
memset(vis,0,sizeof(vis));
n=read();
k=read();
l=(read()-1)/k;
r=read()/k;
int lst,cnt=0;
ll ans=0;
for(re i=1; i<=r; i=lst+1) {
int tmpl=l/i,tmpr=r/i;
//注意特判l/i=0的情況,小心RE
if(!tmpl) {
lst=r/tmpr;
} else {
lst=min(l/tmpl,r/tmpr);
}
ans=((ans+fpow(tmpr-tmpl,n,mod)*(Sum(lst)-Sum(i-1)+mod)%mod)+mod)%mod;
// printf("%lld\n",ans);
}
printf("%lld",ans);
return 0;
}