NKOJ 3441 Lucas的數論(杜教篩)

P3441【HN Training 2015 Round5】lucas的數論

問題描述
這裏寫圖片描述
數據範圍

對於100%的數據:n<=1000000000


直接推式子,用到一個公式,這個公式也比較顯然,就是根據定義直接得到,注意到不互質的數對乘積也一定會被乘積相同的一個互質數對算到。

Ans=i=1Nj=1Nτ(i×j)τ(i×j)=x|iy|j1[gcd(x,y)=1]

Ans=i=1Nj=1Nx|iy|jp|gcd(x,y)μ(p)=i=1Nj=1Np|gcd(i,j)τ(ip)τ(jp)μ(p)

Ans=p=1Nμ(p)i=1Npτ(i)j=1Npτ(j)=p=1Nμ(p)[i=1Npτ(i)]2

注意到後面是取整的形式,如果知道τ(i)μ(i) 的前綴和,那麼可以分塊在O(n) 內求解
考慮如何快速求出τ(i) 的前綴和與μ(i) 的前綴和,考慮杜教篩。

對於μ(i) ,注意到d|nμ(d)=[n==1] ,那麼有i=1nd|iμ(d)=1 ,於是i=1nd=1niμ(d)=1

i=1nμ(d)=1i=2nd=1niμ(d)i=1nμ(i)=M(n)

M(n)=1i=2nM(ni)

上式顯然可以分塊迭代處理,預處理一部分後做到O(n23) ,從狄利克雷卷積的角度就是μI=e

對於τ(i) ,注意到τ(i)=d|i1 ,即τ=II ,那麼μτ=Ie=I ,即d|nμ(nd)τ(d)=1 ,同樣有i=1nd|iμ(nd)τ(d)=i=1nd=1niμ(i)τ(d)=1 ,於是

i=1nτ(i)=1i=2nμ(i)d=1niτ(d)T(n)=i=1nτ(i)

T(n)=1i=2nμ(i)T(ni)

上式顯然還是分塊迭代處理,但是需要用到μ(i) 的前綴和,複雜度不好說,但還是比較快的。

關於預處理一部分tau(i) ,線性篩的時候需要增加兩個數組,一個記錄i 的最小質因子,另一個記錄最小質因子的指數,由於線性篩每次篩掉一個數一定是用他的最小質因數,因此可以方便的轉移,具體可以看代碼。

事實上,求τ(i) 的前綴和有更快的方法,因爲i=1nτ(i)=i=1nd|i1=i=1nd=1ni1=i=1nni ,直接分塊+預處理可以做到O(n) ,最終複雜度O(n34) ,複雜度並不會證,看看就好。


代碼(杜教篩求τ(i) ):

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#define N 12345678
#define ll long long
using namespace std;
const ll mod=1e9+7;
map<ll,ll>Mu,G;
ll n,K,tot,P[N],g[N],mu[N],pc[N],pd[N];
void EU()
{
    ll i,j;mu[1]=g[1]=1;
    for(i=2;i<=K;i++)
    {
        if(!g[i])P[++tot]=i,mu[i]=-1,g[i]=2,pc[i]=1,pd[i]=tot;
        for(j=1;j<=tot&&i*P[j]<=K;j++)
        {
            if(i%P[j])
            {
                pc[i*P[j]]=1;
                pd[i*P[j]]=j;
                mu[i*P[j]]=-mu[i];
                g[i*P[j]]=g[i]<<1;
            }
            else
            {
                pc[i*P[j]]=j==pd[i]?pc[i]+1:1;
                pd[i*P[j]]=j;
                g[i*P[j]]=j==pd[i]?(g[i]/(pc[i]+1)*(pc[i]+2)):(g[i]<<1);
            }
        }
    }
    for(i=2;i<=K;i++)g[i]+=g[i-1],mu[i]+=mu[i-1],g[i]%=mod;
}
ll Gmu(ll x)
{
    if(x<=K)return mu[x];
    if(Mu[x])return Mu[x];
    ll i,j,ans=1;
    for(i=2;i<=x;i=j+1)
    {
        j=x/(x/i);
        ans-=(j-i+1)*Gmu(x/i);
    }
    return Mu[x]=ans;
}
ll Gg(ll x)
{
    if(x<=K)return g[x];
    if(G[x])return G[x];
    ll i,j,ans=x;
    for(i=2;i<=x;i=j+1)
    {
        j=x/(x/i);
        ans-=(Gmu(j)-Gmu(i-1))*Gg(x/i)%mod;ans%=mod;
    }
    return G[x]=ans;
}
int main()
{
    ll ans=0,i,j;
    scanf("%lld",&n);
    K=pow(n,0.66);EU();
    for(i=1;i<=n;i=j+1)
    {
        j=n/(n/i);
        ans+=(Gmu(j)-Gmu(i-1))*Gg(n/i)%mod*Gg(n/i)%mod;ans%=mod;
    }
    printf("%lld",(ans+mod)%mod);
}

另附O(n)τ 做法,實測並快不了多少,但如果是算單個應該快很多。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#define N 12345678
#define ll long long
using namespace std;
const ll mod=1e9+7;
map<ll,ll>Mu,G;
ll n,K,tot,P[N],g[N],mu[N],pc[N],pd[N];
void EU()
{
    ll i,j;mu[1]=g[1]=1;
    for(i=2;i<=K;i++)
    {
        if(!g[i])P[++tot]=i,mu[i]=-1,g[i]=2,pc[i]=1,pd[i]=tot;
        for(j=1;j<=tot&&i*P[j]<=K;j++)
        {
            if(i%P[j])
            {
                pc[i*P[j]]=1;
                pd[i*P[j]]=j;
                mu[i*P[j]]=-mu[i];
                g[i*P[j]]=g[i]<<1;
            }
            else
            {
                pc[i*P[j]]=j==pd[i]?pc[i]+1:1;
                pd[i*P[j]]=j;
                g[i*P[j]]=j==pd[i]?(g[i]/(pc[i]+1)*(pc[i]+2)):(g[i]<<1);
            }
        }
    }
    for(i=2;i<=K;i++)g[i]+=g[i-1],mu[i]+=mu[i-1],g[i]%=mod;
}
ll Gmu(ll x)
{
    if(x<=K)return mu[x];
    if(Mu[x])return Mu[x];
    ll i,j,ans=1;
    for(i=2;i<=x;i=j+1)
    {
        j=x/(x/i);
        ans-=(j-i+1)*Gmu(x/i);
    }
    return Mu[x]=ans;
}
ll Gg(ll x)
{
    if(x<=K)return g[x];
    if(G[x])return G[x];
    ll ans=0,i,j;
    for(i=1;i<=x;i=j+1)
    {
        j=x/(x/i);
        ans+=(j-i+1)*(x/i)%mod;ans%=mod;
    }
    return G[x]=ans;
}
int main()
{
    ll ans=0,i,j;
    scanf("%lld",&n);
    K=pow(n,0.66);EU();
    for(i=1;i<=n;i=j+1)
    {
        j=n/(n/i);
        ans+=(Gmu(j)-Gmu(i-1))*Gg(n/i)%mod*Gg(n/i)%mod;ans%=mod;
    }
    printf("%lld",(ans+mod)%mod);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章