版权声明:转载注明出处,谢谢,有问题可以向博主联系 https://blog.csdn.net/VictoryCzt/article/details/85090284
【题目地址】
题意简述
给定 求下面式子在 意义下的值。
通过莫比乌斯反演的套路,我们很容易将式子变成如下形式:
然后将莫比乌斯函数套入,得到:
我们用杜教筛筛前面的 的前缀和,后面分块算即可,复杂度 。
代码~~
#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=2e6+10;
const ll Mod=1e9+7;
ll n,K,L,H,up;
ll prime[M],mu[M],cnt;
bool vis[M];
void init(){
mu[1]=1;
for(int i=2;i<=up;i++){
if(!vis[i]){
prime[++cnt]=i;
mu[i]=Mod-1;
}
for(int j=1,v;j<=cnt&&i*prime[j]<=up;j++){
v=i*prime[j];
vis[v]=1;
if(!(i%prime[j])){
break;
}
mu[v]=(Mod-mu[i]);
}
}
for(int i=2;i<=up;i++)mu[i]=(mu[i]+mu[i-1])%Mod;
}
ll fpow(ll a,ll b){
ll res=1;
for(;b;b>>=1,a=(a*a)%Mod)
if(b&1)res=(res*a)%Mod;
return res;
}
map <ll,ll> mp;
ll calc(ll x){
if(x<=up) return mu[x];
if(mp.count(x)) return mp[x];
ll ans=1;
for(ll i=2,j;i<=x;i=j+1){
j=(x/(x/i));
ans=(ans-((j-i+1)%Mod*calc(x/i)%Mod))%Mod;
}
if(ans<0)ans+=Mod;
return mp[x]=ans;
}
ll solve(){
up=min(H/K+1,M-1ll);
init();
ll Max=H/K,ans=0;
ll t1=H/K,t2=(L-1)/K;
for(ll i=1,j;i<=Max;i=j+1){
if(t2/i)j=min(t1/(t1/i),t2/(t2/i));
else j=t1/(t1/i);
ans=(ans+(calc(j)-calc(i-1))%Mod*fpow((t1/i-t2/i),n)%Mod)%Mod;
}
if(ans<0)ans+=Mod;
return ans;
}
int main(){
scanf("%lld%lld%lld%lld",&n,&K,&L,&H);
printf("%lld\n",solve());
return 0;
}
但是我们发现,题面上还有个 的条件,由于在 中选一些不同的数出来的 ,那么就可以枚举 ,所以我们可以令 为最大公约数为 的答案。
关于为什么在 中选一些不同的数出来的 不会大于 ,这里简单证明:
首先我们只用考虑选两个数出来,如果这两个数的 ,那么令小的那个为 ,大的为 ,那么由于它们两个不同,所以 ,所以它们两个之间的距离一定大于了 ,所以不可能同时在 内。扫描二维码关注公众号,回复: 4566440 查看本文章
此时根据前面的转换,最后可以变成答案为 。
下面我们考虑如何求取 :
我们从大到小枚举 ,我们可以发现,凡是为 的倍数的数都有可能成为答案,所以先加上所有的 的倍数组合后的结果 ,但是会多算一些,也就是最大公约数为 的方案,所以最后再减去 即可。
复杂度
注意 :最后如果 在 之间的话还要加上全部为 的方案数,也就是1。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=1e5+10;
const ll Mod=1e9+7;
ll n,K,L,H,f[M],len,ls;
ll fpow(ll a,ll b){
ll res=1;
for(;b;b>>=1,a=(a*a)%Mod){
if(b&1)res=(res*a)%Mod;
}
return res;
}
int main(){
scanf("%lld%lld%lld%lld",&n,&K,&L,&H);
if(L<=K&&K<=H)ls=1;
ll l=(L-1)/K,r=H/K;
len=r-l;
for(int i=len;i>=1;i--){
ll x=l/i,y=r/i;
f[i]=fpow(y-x,n)-(y-x);
if(f[i]<0)(f[i]%=Mod)+=Mod;
for(ll j=i+i;j<=len;j+=i){
f[i]=(f[i]-f[j])%Mod;
}
if(f[i]<0)f[i]+=Mod;
}
printf("%lld\n",f[1]+ls);
return 0;
}