莫比乌斯函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luyehao1/article/details/82661129

莫比乌斯函数:

\mu (i)=\left\{\begin{matrix} 1 , i=1 & \\ (-1)^{k},i=p1*p2*...*pk& \\ 0,rest& \end{matrix}\right.

其中, pi 表示质数。

性质:

1.

[n=1]=\sum_{d|n}^{ }\mu (d)

=>  [gcd(a,b)=1]=\sum_{d|gcd(a,b)}^{ }\mu (d)

2.

\sum_{d|n}^{ }\frac{\mu (d)}{d}=\frac{\phi (n)}{n}

3.

若a,b互质,那么\mu (ab)=\mu (a)*\mu (b)

4.

莫比乌斯反演:

g(x)=\sum_{x|d}^{ }f(d) ,则 f(x)=\sum_{x|d}^{ }\mu (\frac{d}{x})*g(d) .

\mu (x)

1. 打表:

//线性筛法求莫比乌斯函数  
bool check[MAX+10];  
int prime[MAX+10];  
int mu[MAX+10];  
void Moblus()  
{  
    memset(check,false,sizeof(check));  
    mu[1] = 1;  
    int tot = 0;  
    for(int i = 2; i <= MAX; i++)  
    {  
        if( !check[i] ){  
            prime[tot++] = i;  
            mu[i] = -1;  
        }  
        for(int j = 0; j < tot; j++)  
        {  
            if(i * prime[j] > MAX) break;  
            check[i * prime[j]] = true;  
            if( i % prime[j] == 0){  
                mu[i * prime[j]] = 0;  
                break;  
            }else{  
                mu[i * prime[j]] = -mu[i];  
            }  
        }  
    }  
}

2. 非打表:

ll getmob(ll a){
    ll x=a,tmp=a;
    int cnt=0,now=0;
    for(ll j=2;j*j<=x;j++){
        now=0;
        if(x%j==0){
            while(x%j==0) now++,x/=j;
            if(now>1) return 0;
            cnt++;
        }
    }
    if(x!=1) cnt++;
    return (cnt&1)?-1:1;
}

应用:

1.

\sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j)=\sum_{i=1}^{n}\sum_{j=1}^{m}\frac{ij}{gcd(i,j)}

=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d=1}^{n}[gcd(i,j)=d]*\frac{ij}{d}=\sum_{d=1}^{n}\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=d]*\frac{ij}{d}

=\sum_{d=1}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{m}{d} \right \rfloor}[gcd(i,j)=1]*i*j*d=\sum_{d=1}^{n}d\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{m}{d} \right \rfloor}[gcd(i,j)=1]*i*j

f(k)=\sum_{i=1}^{x}\sum_{j=1}^{y}[gcd(i,j)=k]*i*jg(k)=\sum_{k|d}^{ }f(d)

=> f(k)=\sum_{k|d}^{ }\mu (\frac{d}{k})*g(d)  (莫比乌斯反演)

=> f(1)=\sum_{1|d}^{ }\mu (d)*g(d)=\sum_{d=1}^{x}\mu (d)*g(d)

g(k)=\sum_{k|d}^{ }f(d)=\sum_{k|d}^{ }\sum_{i=1}^{x}\sum_{j=1}^{y}[gcd(i,j)=d]*i*j

=\sum_{i=1}^{x}\sum_{j=1}^{y}[k|gcd(i,j)]*i*j=\sum_{i=1}^{\left \lfloor \frac{x}{k} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{y}{k} \right \rfloor}[1|gcd(i,j)]*i*j*k^{2}

=k^{2}\sum_{i=1}^{\left \lfloor \frac{x}{k} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{y}{k} \right \rfloor}[1|gcd(i,j)]*i*j=k^{2}\sum_{i=1}^{\left \lfloor \frac{x}{k} \right \rfloor}i\sum_{j=1}^{\left \lfloor \frac{y}{k} \right \rfloor}j=k^{2}*\frac{\left \lfloor \frac{x}{k} \right \rfloor*(\left \lfloor \frac{x}{k} \right \rfloor+1)}{2}*\frac{\left \lfloor \frac{y}{k} \right \rfloor*(\left \lfloor \frac{y}{k} \right \rfloor+1)}{2}

综上:\sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j)=\sum_{d=1}^{n}d\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\mu (i)*i^{2}*S(\left \lfloor \frac{\left \lfloor \frac{n}{d} \right \rfloor}{i} \right \rfloor)*S(\left \lfloor \frac{\left \lfloor \frac{m}{d} \right \rfloor}{i} \right \rfloor),其中S(x)=\frac{x*(x+1)}{2}

这个式子按暴力去算是O(n(1+1/2+1/3+...+1/n)),约等于O(nlogn)的复杂度,往往不能达到要求。可以用数论分块优化成O(n)的复杂度。

本题数论分块方法:\sum_{i=1}^{n} \left \lfloor \frac{n}{i} \right \rfloor 这个形式的式子可用O(sqrt(n))求得,因为该式子只有O(sqrt(n))个值,且若\left \lfloor \frac{n}{i} \right \rfloor=k,那么在 [\left \lfloor \frac{n}{k+1} \right \rfloor+1,\left \lfloor \frac{n}{k} \right \rfloor] 范围内 \left \lfloor \frac{n}{i} \right \rfloor 的值均为 k 。因此,先预处理 \mu (i)*i^{2} 的前缀和后,本题后面那个累加可以在O(sqrt(n))的复杂度下求得,前面那个累加也可以在O(sqrt(n))下求得,总复杂度为O(n) 。

code:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 1e7+10;
const ll mod=20101009;

ll n,m;
bool check[MAX];
int prime[MAX];
int mu[MAX];
ll sum[MAX];

void Moblus()
{
    memset(check,false,sizeof(check));
    mu[1] = 1;
    int tot = 0;
    for(int i = 2; i <= n; i++)
    {
        if( !check[i] ){
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < tot; j++)
        {
            if(i * prime[j] > n) break;
            check[i * prime[j]] = true;
            if( i % prime[j] == 0){
                mu[i * prime[j]] = 0;
                break;
            }else{
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
    sum[0]=0;
    for(int i=1;i<=n;i++){
        sum[i]=(sum[i-1]+mu[i]*(1ll*i*i%mod)+mod)%mod;
    }
}

ll get_g(ll x,ll y)
{
    ll tmp1=x*(x+1)/2%mod;
    ll tmp2=y*(y+1)/2%mod;
    ll ans=tmp1*tmp2%mod;
    return ans;
}

ll getlast(ll x,ll y)
{
    ll ans=0;
    ll pos=0;
    for(int i=1;i<=x;i=pos+1){
        pos=min(x/(x/i),y/(y/i));
        ans=(ans+(sum[pos]-sum[i-1]+mod)%mod*get_g(x/i,y/i)%mod)%mod;
    }
    return ans;
}

int main()
{
	scanf("%lld%lld",&n,&m);
	if(n>m) swap(n,m);
	Moblus();
	ll ans=0;
	ll pos=0;
	for(int i=1;i<=n;i=pos+1){
        pos=min(n/(n/i),m/(m/i));
        ans=(ans+1ll*(i+pos)*(pos-i+1)/2%mod*getlast(n/i,m/i)%mod)%mod;
	}
	printf("%lld\n",ans);
	return 0;
}

如果本题有多组数据,那么还可以O(n)预处理,每次询问O(sqrt(n))

\sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j)=\sum_{d=1}^{n}d\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\mu (i)*i^{2}*S(\left \lfloor \frac{\left \lfloor \frac{n}{d} \right \rfloor}{i} \right \rfloor)*S(\left \lfloor \frac{\left \lfloor \frac{m}{d} \right \rfloor}{i} \right \rfloor)

=\sum_{d=1}^{n}d\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\mu (i)*i^{2}*S(\left \lfloor \frac{n}{id} \right \rfloor)*S(\left \lfloor \frac{m}{id} \right \rfloor)

设 T=id,则原式 = \sum_{d=1}^{n}d\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\mu (i)*i^{2}*S(\left \lfloor \frac{n}{T} \right \rfloor)*S(\left \lfloor \frac{m}{T} \right \rfloor)

=\sum_{T=1}^{n}S(\left \lfloor \frac{n}{T} \right \rfloor)*S(\left \lfloor \frac{m}{T} \right \rfloor)*\sum_{d|n}^{ }\frac{T}{d}*d^{2}*\mu (d)

因为 \frac{T}{d}d^{2}*\mu (d) 都是积性函数,所以他们的狄利克雷卷积也是积性函数,即\sum_{d|n}^{ }\frac{T}{d}*d^{2}*\mu (d)为积性函数,所以可以用线性筛预处理它的前缀和,然后等式前半段通过对 \left \lfloor \frac{n}{i} \right \rfloor 的数论分块O(sqrt(n))求得,因此单次询问复杂度O(sqrt(n))。

code:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 1e7+10;
const ll mod=20101009;

ll n,m;
ll sum[MAX],check[MAX],prime[MAX];

void get_sum()
{
    memset(check,0,sizeof(check));
    sum[1]=1;
    int tot=0;
    for(int i=2;i<=n;i++){
        if(!check[i]){
            prime[tot++]=i;
            sum[i]=(i-1ll*i*i%mod+mod)%mod;
        }
        for(int j=0;j<tot&&i*prime[j]<=n;j++){
            check[i*prime[j]]=1;
            if(i%prime[j]==0){
                sum[i*prime[j]]=sum[i]*prime[j]%mod;
                break;
            }
            else{
                sum[i*prime[j]]=sum[i]*sum[prime[j]]%mod;
            }
        }
    }
    for(int i=2;i<=n;i++)
        sum[i]=(sum[i-1]+sum[i])%mod;
}

int main()
{
    scanf("%lld%lld",&n,&m);
    get_sum();
    if(n>m) swap(n,m);
    ll pos=0;
    ll ans=0;
    for(int i=1;i<=n;i=pos+1){
        pos=min(n/(n/i),m/(m/i));
        ll tmp=((n/i)*(n/i+1)/2%mod)*((m/i)*(m/i+1)/2%mod)%mod;
        ans=(ans+(sum[pos]-sum[i-1]+mod)%mod*tmp%mod)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

2.

\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=p] , p为质数,n,m<=1e7,1000组询问 。 (洛谷2257)

推导过程略。

=> 原式 =  \sum_{T=1}^{n}\left \lfloor \frac{n}{T} \right \rfloor*\left \lfloor \frac{m}{T} \right \rfloor*\sum_{p|T}^{ }\mu (\frac{T}{p}),p为质数。

后半部分预处理,前半部分数论分块,每次查询O(sqrt(n)) 。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 10000000;
const ll MOD = 1000000007;

ll mu[MAX+10],prime[MAX+10];
ll sum[MAX];
bool check[MAX+10];

inline int read()
{
    int x=0,t=1;register char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}

//预处理前缀和
void get_sum()
{
    memset(check,false,sizeof(check));
    mu[1]=1;
    int tot=0;
    for(int i=2;i<=MAX;i++){
        if(!check[i]){
            prime[tot++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<tot&&i*prime[j]<=MAX;j++){
            check[i*prime[j]]=true;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;
                break;
            }
            else{
                mu[i*prime[j]]=-mu[i];
            }
        }
    }
    sum[0]=sum[1]=0;
    for(int  j=0;j<tot;j++){
        for(int i=1;i*prime[j]<=MAX;i++){
            sum[i*prime[j]]+=mu[i];
        }
    }
    for(int i=2;i<=MAX;i++)
        sum[i]+=sum[i-1];
}

int main()
{
    get_sum();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        n=read();
        m=read();
        if(n>m) swap(n,m);
        int pos;
        ll ans=0;
        //数论分块
        for(int i=1;i<=n;i=pos+1){
            pos=min(n/(n/i),m/(m/i));
            ans+=1ll*(n/i)*(m/i)*(sum[pos]-sum[i-1]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

3. \sum_{i=1}^{m}\mu (i*n) (1<=m<=2e9,1<=n<=1e12)

\sum_{i=1}^{m}\mu (i*n)=\mu (n)*\sum_{d|n}^{ }\mu (d)*\sum_{i=1}^{\left \lfloor m/d \right \rfloor}\mu (i*d)

f(m,n)=\mu (n)*\sum_{d|n}^{ }f(\left \lfloor \frac{m}{d} \right \rfloor,d)

递归终止条件:

m=0:f(m,n)=0

m=1:f(m,n)=\mu (n)

n=1:f(m,n)=\sum_{i=1}^{m}\mu (i) (杜教筛处理)

代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 1e7;

ll mu[MAX+10],prime[MAX+10];
bool check[MAX+10];

void get_sum()
{
    memset(check,false,sizeof(check));
    mu[1]=1;
    int tot=0;
    for(int i=2;i<=MAX;i++){
        if(!check[i]){
            prime[tot++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<tot&&i*prime[j]<=MAX;j++){
            check[i*prime[j]]=true;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;
                break;
            }
            else{
                mu[i*prime[j]]=-mu[i];
            }
        }
    }
    for(int i=2;i<=MAX;i++)
        mu[i]+=mu[i-1];
}

map<ll,ll>dp;

//杜教筛
ll cal(ll x)
{
    if(x<=MAX)   return mu[x];
    if(dp[x])   return dp[x];
    ll pos;
    ll tmp=0;
    for(ll i=2;i<=x;i=pos+1){
        pos=x/(x/i);
        tmp+=(pos-i+1)*cal(x/i);
    }
    return dp[x]=1-tmp;
}

int cnt[5000];
ll val[5000];
ll p[20];
int pnum;

ll u(ll x)
{
    return ((cnt[x]&1)?-1:1);
}

ll dfs(ll m,ll n)
{
    if(cnt[n]==0)    return cal(m);
    if(m==0)    return 0;
    if(m==1)    return u(n);
    ll ans=0;
    //遍历n的所有因子
    for(int i=n;;i=(i-1)&n){
        ans+=u(i)*dfs(m/val[i],i);
        if(!i)    break;
    }
    return ans*u(n);
}

int main()
{
    get_sum();
    ll m,n;
    scanf("%lld%lld",&m,&n);
    int fg=0;
    pnum=0;
    for(ll i=2;i*i<=n;i++){
        if(n%i==0){
            ll tmp=i*i;
            if(n%tmp==0)   {fg=1;break;}
            p[pnum++]=i; //处理出所有素因子
            while(n%i==0)   n/=i;
        }
    }
    if(fg){
        printf("0\n");
    }
    else{
        if(n>1) p[pnum++]=n;
        for(int i=0;i<(1<<pnum);i++){
            cnt[i]=0;//代表一个数含有素因子的个数   
            val[i]=1;//代表这个数的十进制值
            for(int j=0;j<pnum;j++){
                if(i&(1<<j)) cnt[i]++,val[i]*=p[j];
            }
        }
        ll ans=dfs(m,(1<<pnum)-1);
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/luyehao1/article/details/82661129
今日推荐