选数
Description
我们知道,从区间
[L,H]
(
L
和
小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的
N
个整数都求一次最大公约数,以便进一步研究。
然而他很快发现工作量太大了,于是向你寻求帮助。
你的任务很简单,小z会告诉你一个整数
由于方案数较大,你只需要输出其除以
Input
输入一行,包含
4
个空格分开的正整数,依次为
Output
输出一个整数,为所求方案数。
Sample Input
2 2 2 4
Sample Output
3
HINT
样例解释
所有可能的选择方案:
其中最大公约数等于
2
的只有
数据范围
对于 100% 的数据, 1≤N,K≤109 , 1≤L≤H≤109 , H−L≤105
Solution
设
f(d)
表示选取的数最大公约数为
d
的方案数,则
然后就可以像上一题一样,维护一下
μ
函数的前缀和,分块优化。
可是这里的前缀和不好求,怎么办呢?
定义梅滕斯函数
M(n)=∑ni=1μ(i)
,给定正整数
n
,计算
因此 M(n)=1−∑ni=2M(⌊ni⌋) ,问题可在 O(n23) 时间复杂度下解决1。
Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#define LL long long
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define Min(x,y) ((x)<(y)?(x):(y))
using namespace std;
LL n,k,l,h,ans;
LL minx;
bool no_prime[10000010];
LL prime[10000010];
LL miux[10000010];
LL sum[10000010];
map<int,int>hm;
LL power(LL x,LL y){
if(y==0)return 1;
if(y==1)return x%MOD;
LL tmp=power(x,y/2);
if(y&1)return tmp*tmp%MOD*(x%MOD)%MOD;
else return tmp*tmp%MOD;
}
LL solve(LL x){
LL ttt=0;
if(x<=minx)return sum[x];
if(hm.find(x)!=hm.end())return hm[x];
for(LL i=2,last;i<=x;i=last+1){
last=x/(x/i);
ttt+=solve(x/i)*(last-i+1);
}
hm[x]=1-ttt;
return 1-ttt;
}
int main(){
scanf("%lld%lld%lld%lld",&n,&k,&l,&h);
if(l>h)swap(l,h);
minx=Min(h,1e+7);
miux[1]=1;
for(LL i=2;i<=minx;i++){
if(!no_prime[i]){
prime[++prime[0]]=i;
miux[i]=-1;
}
for(LL j=1;j<=prime[0];j++){
if(i*prime[j]>minx)break;
no_prime[i*prime[j]]=true;
if(i%prime[j]==0){miux[i*prime[j]]=0;break;}
miux[prime[j]*i]=-miux[i];
}
}
for(LL i=1;i<=minx;i++)sum[i]+=sum[i-1]+miux[i];
h/=k;l=(l-1)/k;
for(LL i=1,last;i<=h;i=last+1){
last=Min(h/(h/i),((l/i==0?INF:l/(l/i))));
ans=((ans+((solve(last)-solve(i-1))*power(h/i-l/i,n)%MOD))%MOD+MOD)%MOD;
}
printf("%lld\n",ans);
return 0;
}