习题:选数(杜教筛&莫比乌斯反演)
题目
思路
最大公约数是1实际上和最大公约数的k没有区别
考虑最大公约数是1的情况
先看一下两个数的情况
\[\begin{aligned}\sum_{i=l}^r\sum_{j=l}^{r}\sum_{d|gcd(i,j)=1}\mu(d)=\sum_{d=1}^{r}\mu(d)(\lfloor\frac{r}{d}\rfloor-\lfloor\frac{l-1}{d}\rfloor)^2\end{aligned}
\]
然后是三个数的情况
\[\begin{aligned}\sum_{i=l}^r\sum_{j=l}^{r}\sum_{k=l}^{r}\sum_{d|gcd(i,j)=1}\mu(d)=\sum_{d=1}^{r}\mu(d)(\lfloor\frac{r}{d}\rfloor-\lfloor\frac{l-1}{d}\rfloor)^3\end{aligned}
\]
然后针对n个数就有
\(ans=\sum_{d=1}^{r}\mu(d)(\lfloor\frac{r}{d}\rfloor-\lfloor\frac{l-1}{d}\rfloor)^n\)
之后只需要求\(\sum_{i=1}^n\mu(i)\)即可,杜教筛即可,虽然每一次杜教筛理论上是\(O(n^{\frac{2}{3}})\)
但是注意到我们实际上算的前缀和都是n的因数,故实际上总的时间复杂度还是\(O(n^{\frac{2}{3}})\)
代码
#include<unordered_map>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int mod=1e9+7;
int T;
int n,k,hei,low;
int len;
int mu[2000005];
int lenp,pri[2000005];
bool vis[2000005];
long long mmu[2000005];
unordered_map<int,long long> m2;
long long qkpow(int a,int b)
{
if(b==0)
return 1;
if(b==1)
return a;
long long t=qkpow(a,b/2);
t=t*t%mod;
if(b&1)
t=t*a%mod;
return t;
}
void prepa(int n)
{
len=n;
mmu[1]=mu[1]=1;
for(int i=2;i<=n;i++)
{
if(vis[i]==0)
{
pri[++lenp]=i;
mu[i]=-1;
}
for(int j=1;j<=lenp&&1ll*pri[j]*i<=n;j++)
{
vis[i*pri[j]]=1;
if(i%pri[j]==0)
{
mu[i*pri[j]]=0;
break;
}
mu[i*pri[j]]=mu[i]*mu[pri[j]];
}
mmu[i]=((mmu[i-1]+mu[i])%mod+mod)%mod;
}
}
long long getmu(int n)
{
// cout<<"into:";
if(n<=len)
return mmu[n];
if(m2.count(n))
return m2[n];
long long temp=1;
for(long long l=2,r;l<=n;l=r+1)
{
r=min(1ll*n/(n/l),1ll*n);
temp=(temp-1ll*(r-l+1)*getmu(n/l)%mod)%mod;
}
temp=(temp%mod+mod)%mod;
return m2[n]=temp;
}
int main()
{
prepa(pow((1ll<<31)-1,2.0/3));
cin>>n>>k>>low>>hei;
hei/=k;
if(low%k==0)
low=low/k-1;
else
low=low/k;
long long ans=0;
for(int l=1,r;l<=hei;l=r+1)
{
if(low/l==0)
r=min(hei/(hei/l),hei);
else
r=min(min(hei/(hei/l),low/(low/l)),hei);
ans=(ans+1ll*(getmu(r)-getmu(l-1))*qkpow((hei/l-low/l),n)%mod)%mod;
}
cout<<(ans%mod+mod)%mod;
return 0;
}