这道题,常规做法的突破点在于:(R-L<=1e5)这个条件
那么,如果我们把这个条件去掉,询问数据为:
1000000000 1 1 1000000000
该怎么做呢?
一开始,我们先按套路,将L-R取最大公约数为k,化成 到取最大公约数为1
然后开始做题。
首先,我们先来将答案表达式划出来:
(L和R为化过之后的)
那么根据套路带入莫比乌斯函数的性质
然后更改枚举项,枚举k
然后,我们通过观察可以发现,是可以通过整数分块实现的,于是我们整数分块来搞后面部分,至于前面,我们就需要求u的一个区间之和了,这时,我们想到,我们可以求u的前缀和来算区间之和,而快速求u的前缀和的方法,不正是杜教筛/州阁筛/min25筛么?
于是我们就把这题做好了~
(实现时有些小细节需要注意)
代码:
#include<bits/stdc++.h> using namespace std; #define int long long unordered_map<int,int>sp; const int N=1e7,mod=1e9+7; int u[N],zhi[N],e;bool f[N]; inline int ksm(int x,int y){ int ans=1; while(y){ if(y&1){ ans=(1LL*ans*x)%mod; } x=(1LL*x*x)%mod; y>>=1; } return ans; } inline void sai(int maxe){ u[1]=1; for(int i=2;i<=maxe;++i){ if(!f[i]){ u[i]=-1;zhi[++e]=i; } for(int j=1;j<=e;++j){ if(i*zhi[j]>maxe){ break; } f[i*zhi[j]]=1;u[i*zhi[j]]=-u[i]; if(i%zhi[j]==0){ u[i*zhi[j]]=0; break; } } } for(int i=2;i<=maxe;++i){ u[i]+=u[i-1]; } } inline int calc(int x){//杜教筛 if(x<N){ return u[x]; } if(sp.find(x)!=sp.end()){ return sp[x]; } int ans=0; for(int r,l=2;l<=x;l=r+1){ r=(x/(x/l)); ans+=(r-l+1)*calc(x/l); } sp[x]=1-ans; return 1-ans; } signed main(){ sai(N-1);//预处理,按理说应该处理n^(2/3),不过这题主要复杂度不止杜教筛本身,所以我们尽量预处理大点比较好 int n,k,L,R; scanf("%lld%lld%lld%lld",&n,&k,&L,&R); L=ceil(double(L)/k),R/=k; if(!R){ puts("0"); return 0; } int ans=0; for(int r,l=1;l<=R;l=r+1){//整数分块 if(L-1<l){//当这个情况时,如果计算(L-1)/((L-1)/l))的话,会导致除0,注意到,这之后(L-1)/l都不变(为0),所以,我们只需算R/(R/l)即可~ r=(R/(R/l)); }else{ r=min(R/(R/l),(L-1)/((L-1)/l)); } int now=ksm(((R/l)-(L-1)/l),n),fow=calc(r)-calc(l-1); ans=(ans+((1LL*now*fow)%mod))%mod; } ans=(ans+mod)%mod;//注意模成正数 printf("%lld",ans); return 0; }