习题:选数(杜教筛&莫比乌斯反演)

题目

传送门

思路

最大公约数是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;
}

posted @ 2021-01-31 18:10  loney_s  阅读(83)  评论(0)    收藏  举报