题目:
题解:
以前好像这样干过,设F(i)表示在范围内选n个gcd为i的方案数,f(i)表示在范围内选n个i|gcd的方案数
首先
f(d)=∑d|nF(n)
f
(
d
)
=
∑
d
|
n
F
(
n
)
,我们要求的是F(k)
反演
F(k)=∑k|d′f(d′)μ(d′k)
F
(
k
)
=
∑
k
|
d
′
f
(
d
′
)
μ
(
d
′
k
)
显然根据性质
f(d)=(Hd−L−1d)n
f
(
d
)
=
(
H
d
−
L
−
1
d
)
n
反演的柿子d’的上界是H,而且必须是k的倍数
设d’=kd,换元
我们可以处理一下这里的H,L了,H=H/k,L=(L-1)/k,所以柿子是 F(k)=∑Hd=1μ(d)(Hd−Ld)n F ( k ) = ∑ d = 1 H μ ( d ) ( H d − L d ) n
就是很明显的杜教筛了,然后用分块优化除法就好了
代码:
#include <map>
#include <cstdio>
#include <iostream>
#define LL long long
using namespace std;
const int maxn=1e6;
const int N=1000005;
const int mod=1e9+7;
map<LL,LL>mp;
int pri[N],tot,mu[N];bool ss[N];
LL ksm(LL a,LL k)
{
LL ans=1;
for (;k;k>>=1,a=a*a%mod)
if (k&1) ans=ans*a%mod;
return ans;
}
void init(int n)
{
mu[1]=1;
for (int i=2;i<=n;i++)
{
if (!ss[i]) pri[++tot]=i,mu[i]=-1;
for (int j=1;j<=tot && pri[j]*i<=n;j++)
{
ss[pri[j]*i]=1;
if (i%pri[j]==0) break;
mu[pri[j]*i]=-mu[i];
}
}
for (int i=1;i<=n;i++) mu[i]+=mu[i-1];
}
LL getmu(LL n)
{
if (n<=maxn) return mu[n];
if (mp.count(n)) return mp[n];
LL ans=1;
for (int i=2,last;i<=n;i=last+1)
{
last=n/(n/i);
ans=(ans-getmu(n/i)*(last-i+1)%mod)%mod;
}
return mp[n]=ans;
}
int main()
{
int n,k,L,H;
scanf("%d%d%d%d",&n,&k,&L,&H);
L--; H/=k; L/=k;
init(min(H,maxn));LL ans=0;
for (int i=1,last;i<=H;i=last+1)
{
if (L/i) last=min(H,min(H/(H/i),L/(L/i)));
else last=min(H,H/(H/i));
ans=(ans+ksm(H/i-L/i,n)*(getmu(last)-getmu(i-1)+mod)%mod)%mod;
}
printf("%lld",(ans+mod)%mod);
}