本题的性质在于,如果选了两个数,那么这两个数的最大公约数至大为H-L。(所以需要特判只选了一个数的情况)
先来看一下傻逼的做法:
考虑莫比乌斯反演,i*K对答案的贡献是
μ(i)∗N⌊HiK⌋−⌊L−1iK⌋
,对于所有数相同的情况,我们记一下每个数被多算了几次,最后减去即可。
膜拜一下大神的做法:
莫比乌斯反演考虑的是一个前缀和,但是我们为什么要前缀和呢?我们完全可以直接算以i*K为最大公约数的情况有多少啊,f(i)表示以i*K为最大公约数的情况数,那么答案就是
f(i)=N⌊HiK⌋−⌊L−1iK⌋−∑⌊H−LiK⌋j=1f(ij)
莫比乌斯反演:
#include<cstdio>
#include<iostream>
using namespace std;
int prime[30000],mu[100005];
bool p[100005];
#define Mod 1000000007
typedef long long LL;
LL pow(int a,int x){
LL ans=1,prod=a;
for(;x;x>>=1,prod=prod*prod%Mod)
if(x&1)
ans=ans*prod%Mod;
return ans;
}
#include<cmath>
int cnt[100005];
int main(){
freopen("bzoj_3930.in","r",stdin);
int i,j;
mu[1]=1;
for(i=2;i<=100000;++i){
if(!p[i]){
prime[++prime[0]]=i;
mu[i]=-1;
}
for(j=1;j<=prime[0]&&i*prime[j]<=100000;++j){
p[i*prime[j]]=1;
if(i%prime[j])mu[i*prime[j]]=-mu[i];
else break;
}
}
int N,K,L,H;
scanf("%d%d%d%d",&N,&K,&L,&H);
L=(L-1)/K,H=H/K;
int ans=0,x;
for(i=H-L;--i>0;)
if(mu[i]){
for(j=i*(L/i+1),x=0;j<=H;++x,j+=i)cnt[j-L]+=mu[i];
ans=(ans+mu[i]*pow(x,N))%Mod;
}
for(i=H-L;i>1;--i)
if(cnt[i])
ans=(ans-cnt[i])%Mod;
if(L==0)ans=(ans-(1-cnt[1]))%Mod;
else ans=(ans-cnt[1])%Mod;
cout<<(ans+Mod)%Mod<<endl;
}
dp:
#include<cstdio>
#include<iostream>
using namespace std;
#define Mod 1000000007
typedef long long LL;
int pow(int a,int x){
LL ans=1,prod=a;
for(;x;x>>=1,prod=prod*prod%Mod)
if(x&1)
ans=ans*prod%Mod;
return ans;
}
int f[100005];
int main(){
freopen("cqoi15_number.in","r",stdin);
freopen("cqoi15_number.out","w",stdout);
int N,K,L,H,i,j;
scanf("%d%d%d%d",&N,&K,&L,&H);
L=(L-1)/K,H=H/K;
for(i=H-L-1;i;--i){
//printf("---%d----\n",i);
f[i]=(pow(H/i-L/i,N)-(H/i-L/i))%Mod;
//cout<<pow(H/i-L/i,N)<<"-"<<(H/i-L/i)<<"=";
for(j=i<<1;j<H-L;j+=i)f[i]=(f[i]-f[j])%Mod;
//cout<<f[i]<<endl;
}
printf("%d\n",((f[1]+(L==0))%Mod+Mod)%Mod);
}
总结:
再统计类题目中务必要注意待求函数本身与其前缀和的相互转化,往往有时候有一个会更好求一些。