杜教筛

如果一个积性函数,能够快速求得其狄利克雷卷积的前缀和,则可以在 O(n23) 的时间内快速筛出其前缀和,这种筛法叫做杜教筛。

比如:

d|nμ(d)=[u=1]i=1nd|iμ(d)=1

那么如果要求一个函数

M(n)=i=1nμ(i)

有:
1M(n)=i=1nd|iμ(d)=i=1nd=1niμ(d)=i=1nM(ni)=1i=2nM(ni)

(对于 ϕ(i) d|iϕ(i)=n ,则 M(n)=n(n+1)2i=2nM(ni) )

对于后面的部分,分块递归求解,并用HashTable实现记忆化搜索。

其实本质上相当于求出多个前缀,再减去算多余的前缀,而卷积满足一定性质后能够很方便的算出多个前缀的和,所以这个算法局限性很大,一般只用于求解 ϕ 或者 μ 的前缀和。

  • Code:
    To get M(n)=i=1nϕ(i) in O(n23) time.
#include
using namespace std;
const int Maxn=5e6;
const int MOD=1e9+7;
const int inv=5e8+4;
int phi[Maxn+50],prime[Maxn+50],IsNotPrime[Maxn+50],primecnt;
long long n,m;
inline void seive()
{
    phi[1]=1;
    for(int i=2;i<=Maxn;i++)
    {
        if(!IsNotPrime[i])
        {
            prime[++primecnt]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=primecnt;j++)
        {
            long long k=i*prime[j];
            if(k>Maxn)break;
            IsNotPrime[k]=1;
            if(!(i%prime[j]))
            {
                phi[k]=1ll*phi[i]*prime[j]%MOD;
                break;
            }
            phi[k]=1ll*phi[i]*(prime[j]-1)%MOD;
        }
    }
    for(int i=1;i<=Maxn;i++)(phi[i]+=phi[i-1])%=MOD;
}
const int Mod=481883;
struct Hash_Table
{
    int last[Mod],val[Mod+50],tot,nxt[Mod+50],before[Mod+50];
    long long id[Mod+50];
    inline void insert(long long lim,int v)
    {
        int pos=lim%Mod;
        for(int p=last[pos];p;p=nxt[p]){if(id[p]==lim)return;}
        nxt[++tot]=last[pos];last[pos]=tot;id[tot]=lim;val[tot]=v;
    }
    inline bool find(long long lim,int &v)
    {
        int pos=lim%Mod;
        for(pos=last[pos];pos;pos=nxt[pos]){if(id[pos]==lim){v=val[pos];return true;}}
        return false;
    }
}hashtable;
int tmp;
inline int M(long long lim)
{
    if(!lim)return 0;
    if(lim<=Maxn)return phi[lim];
    else if(hashtable.find(lim,tmp))return tmp;
    int ans=(1ll*lim%MOD*((lim+1)%MOD)%MOD*inv)%MOD;
    for(long long bg=2,pos=0;bg<=lim;bg=pos+1)
    {   
        pos=lim/(lim/bg);
        ans-=1ll*(pos-bg+1)%MOD*M(lim/bg)%MOD;
        ans=(ans+MOD)%MOD;
    }
    hashtable.insert(lim,ans);
    return ans;
}

int main()
{
    seive();
    scanf("%lld",&m);
    printf("%d",M(m));
}

你可能感兴趣的:(Mobius反演,杜教筛)