• 杜教筛总结


    杜教筛

    运用狄利克雷卷积的形式对一些积性函数在小于线性的时间内求前缀和。

    套路式

    对于要求前缀和的积性函数(f(i)),假设其前缀和函数为(S(i))。构造积性函数(g(i)),与原函数做狄利克雷卷积得$$(f*g)(i)=sum_{d|i}g(d)f(frac id)$$
    对卷积函数求前缀和

    [sum_{i=1}^{n}(f*g)(i)=sum_{i=1}^{n}sum_{d|i}g(d)f(frac id)\=sum_{d=1}^{n}g(d)sum_{d|i}^{n}f(frac id)\=sum_{d=1}^{n}g(d)sum_{i=1}^{n/d}f(i)\=sum_{d=1}^{n}g(d)S(frac nd) ]

    所以就得到这样一个等式

    [g(1)S(n)=sum_{i=1}^{n}(f*g)(i)-sum_{d=2}^{n}g(d)S(frac nd) ]

    e.g.

    (sum_{i=1}^{n}varphi(i),nle10^{10})
    (S(x)=sum_{i=1}^{x}varphi(i)),则有

    [g(1)S(n)=sum_{i=1}^{n}(f*g)(i)-sum_{d=2}^{n}g(d)S(frac nd) ]

    我们要构造一个函数使得(sum_{i=1}^{n}(f*g)(i))很好求
    利用(sum_{d|n}varphi(d)=n)可知令(g(i)=1)就可以有

    [(f*g)(i)=sum_{d|i}varphi(frac id)=sum_{d|i}varphi(d)=i ]

    [sum_{i=1}^{n}(f*g)(i)=sum_{i=1}^{n}i=frac{i(i+1)}{2} ]

    所以就搞定了。

    关于实现

    写一个求(S(i))的函数,计算的时候递归调用。注意记忆化。
    一般而言都是开map
    Gay神说可以不开map省掉一个log

    以下出自Gay神:

    (qquad)杜教筛其实有个小trick,可以不用写map,省掉一个log,具体是因为你所有要筛出来的前缀和都是可以写成F(n/i)的,前面预处理了的都不用记了,你对于i记忆化,就可以达到快速记下要筛出来的前缀和的目的.

    所以开一个数组记录就可以了。

    (注:经yyb实测表明这并不能优化多少,所以以下两份代码均未使用这种trick)

    模板题

    【BZOJ3944】Sum
    就是求上面讲到的两个东西

    #include<cstdio>
    #include<algorithm>
    #include<map>
    using namespace std;
    #define ll long long
    const int N = 2500000;
    ll gi()
    {
        ll x=0,w=1;char ch=getchar();
        while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
        if (ch=='-') w=0,ch=getchar();
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return w?x:-x;
    }
    int pri[N+5],tot,zhi[N+5];
    ll mu[N+5],phi[N+5];
    map<ll,ll>M,P;
    void Mobius()
    {
        zhi[1]=1;mu[1]=phi[1]=1;
        for (int i=2;i<=N;++i)
        {
            if (!zhi[i]) pri[++tot]=i,mu[i]=-1,phi[i]=i-1;
            for (int j=1;j<=tot&&i*pri[j]<=N;++j)
            {
                zhi[i*pri[j]]=1;
                if (i%pri[j]) mu[i*pri[j]]=-mu[i],phi[i*pri[j]]=phi[i]*phi[pri[j]];
                else {mu[i*pri[j]]=0;phi[i*pri[j]]=phi[i]*pri[j];break;}
            }
        }
        for (int i=1;i<=N;++i)
            mu[i]+=mu[i-1],phi[i]+=phi[i-1];
    }
    ll Phi(ll x)
    {
        if (x<=N) return phi[x];
        if (P[x]) return P[x];
        ll res=0,i=2,j;
        while (i<=x)
        {
            j=x/(x/i);
            res+=(j-i+1)*Phi(x/i);
            i=j+1;
        }
        return P[x]=x*(x+1)/2-res;
    }
    ll Mu(ll x)
    {
        if (x<=N) return mu[x];
        if (M[x]) return M[x];
        ll res=0,i=2,j;
        while (i<=x)
        {
            j=x/(x/i);
            res+=(j-i+1)*Mu(x/i);
            i=j+1;
        }
        return M[x]=1-res;
    }
    int main()
    {
        Mobius();
        int T=gi();
        while (T--)
        {
            int n=gi();
            printf("%lld %lld
    ",Phi(n),Mu(n));
        }
        return 0;
    }
    

    还是模板题

    【BZOJ4916】神犇和蒟蒻
    (sum_{i=1}^{n}mu(i^2))(sum_{i=1}^{n}varphi(i^2))
    第一问答案恒为1.
    第二问,(varphi(i^2)=ivarphi(i)),所以还是同样的套路做。
    (sum_{i=1}^{n}i^2=frac{1}{6}n(n+1)(2n+1))

    #include<cstdio>
    #include<algorithm>
    #include<map>
    using namespace std;
    const int mod = 1e9 + 7;
    const int N = 10000000;
    const int inv = 166666668;
    int gi()
    {
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    int pri[N+5],tot,zhi[N+5],phi[N+5];
    map<int,int>P;
    void Mobius()
    {
    	zhi[1]=phi[1]=1;
    	for (int i=2;i<=N;i++)
    	{
    		if (!zhi[i]) pri[++tot]=i,phi[i]=i-1;
    		for (int j=1;j<=tot&&i*pri[j]<=N;j++)
    		{
    			zhi[i*pri[j]]=1;
    			if (i%pri[j]) phi[i*pri[j]]=1ll*phi[i]*phi[pri[j]]%mod;
    			else {phi[i*pri[j]]=1ll*phi[i]*pri[j]%mod;break;}
    		}
    	}
    	for (int i=1;i<=N;i++) phi[i]=(phi[i-1]+1ll*i*phi[i]%mod)%mod;
    }
    int Phi(int n)
    {
    	if (n<=N) return phi[n];
    	if (P[n]) return P[n];
    	int res=1ll*n*(n+1)%mod*(2*n+1)%mod*inv%mod;
    	int i=2,j;
    	while (i<=n)
    	{
    		j=n/(n/i);
    		res=(res-1ll*(j+i)*(j-i+1)/2%mod*Phi(n/i)+mod)%mod;
    		i=j+1;
    	}
    	return P[n]=res;
    }
    int main()
    {
    	Mobius();
    	int n=gi();
    	printf("1
    %d
    ",Phi(n));
    	return 0;
    }
    
  • 相关阅读:
    Java设计模式—模板方法模式
    STM32 常用GPIO操作函数记录
    GPIO 配置之ODR, BSRR, BRR 详解
    STM32F4先设置寄存器还是先使能时钟
    LDR指令的格式:
    printf函数重定向
    stm32F4各个库文件的作用分析
    STM32F4时钟设置分析
    STM32F407存储器和总线架构
    SPI移位寄存器
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/8301620.html
Copyright © 2020-2023  润新知