杜教筛--[CQOI2015]选数

杜教筛这东西其实思想和莫比乌斯反演十分类似,通过解出比较好求的函数快速推得要求但比较难求的函数。

首先它所用的知识是一个叫狄利克雷卷积的东西,对于两个积性函数f(n),g(n)f(n),g(n),定义它们的狄利克雷卷积为:(f∗g)(n)=∑d|n f(d)∗g(n/d),(就是莫比乌斯反演中原本“1”的位置被改为函数),这样我们可以用(f∗g)和g这两个函数推出我们所要的函数f。

以下转载自:https://www.cnblogs.com/Mychael/p/8744633.html

杜教筛--[CQOI2015]选数_第1张图片

而g在求莫比乌斯反演时是恒为一的函数,(f∗g)因为miu函数和“1”函数互为逆元可推得相乘为单位元,即[n==1],那么这玩意可以先预处理掉一部分答案(线性筛筛个2/3次左右),然后整除分块加记忆化搜索。

例题:[CQOI2015]选数

链接:https://www.luogu.org/problemnew/show/P3172

其实这道题不算杜教筛例题,因为它的标算是利用好h-l1e5,但sw大佬随便就用杜教筛艹过去了,蒟蒻自然要跟大佬看齐了。。这道题用杜教筛的原因是推出的式子里莫比乌斯函数最大可能有1e9,套上杜教筛跑整除分块的莫比乌斯反演就可以过了(复杂度最差是sqrt(1e5)*O(1e9*2/3=1e6)的样子?但实际上远远不到,记忆化搜索杜教筛均摊下来每次应该不会都有O(2/3),反正能过就行了。。

// luogu-judger-enable-o2
#include
using namespace std;
const int mod=1e9+7;
const int N=1e6;
bool vis[N+10];int miu[N+10],prime[N+10],pnum,sum[N+10];
mapvi;
int qpow(int x,int y)
{
    int res=1;
    while(y)
    {
        if(y&1)res=1LL*res*x%mod;
        x=1LL*x*x%mod,y>>=1;
    }
    return res;
}
int ask(int x)
{
    if(x<=N)return sum[x];
    int tp=vi[x];
    if(tp)return tp;
    tp=1;
    for(int l=2,r;l<=x;l=r+1)
    {
        r=x/(x/l);
        tp-=ask(x/l)*(r-l+1);
    }
    return vi[x]=tp;
}
void init()
{
    miu[1]=1;
    for(int i=2;i<=N;i++)
    {
        if(!vis[i])miu[i]=-1,prime[++pnum]=i;
        for(int j=1;j<=pnum&&prime[j]*i<=N;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            miu[i*prime[j]]=-miu[i];
        }
    }
    for(int i=1;i<=N;i++)
        sum[i]=sum[i-1]+miu[i];
}
int main()
{
    int n,k,L,H,ans=0;
    init();
    cin>>n>>k>>L>>H;
    L--,H/=k,L/=k;
    for(int l=1,r;l<=H;l=r+1)
    {
        r=H/(H/l);
        if(L>=l)r=min(r,L/(L/l));
        ans=(1LL*qpow(H/l-L/l,n)*(ask(r)-ask(l-1))%mod+ans)%mod;
    }
    printf("%d\n",(ans+mod)%mod);
}

 

你可能感兴趣的:(笔记)