zoukankan      html  css  js  c++  java
  • 莫比乌斯反演学习笔记

    前置知识

    以下结论是显然的:

    (1)若( heta)可乘,则

    • ( heta(1)=1)
    • ( heta(n)= hetaleft(p 1^{alpha 1} ight) hetaleft(p 2^{alpha 2} ight) ldots hetaleft(p k^{alpha k} ight))
    • ( heta 1, heta 2) 可乘 (Longrightarrow heta 1 heta 2) 可乘

    (2)(sum_{d mid n}):对n所有的正因子求和
    (left(sum_{d mid n} f(d) ight)left(sum_{d mid n} g(d) ight)=sum_{d_1|n wedge d_2| n} f(d 1) g(d 2))

    (3)【莫比乌斯函数】定义:

    [mu(1)=1, mu(n)=muleft(p 1^{alpha 1} ight) muleft(p 2^{alpha 2} ight) ldots muleft(p k^{alpha k} ight)=left{egin{array}{l}(-1)^{k} ext { 若 } alpha 1=alpha 2=ldots=alpha k=1 \ 0 quad ext { otherwise }end{array} ight. ]

    显然,(mu)为可乘函数

    推论:

    (4)若( heta)可乘,则(psi(n)=sum_{d mid n} heta(d))可乘(下文引用此处的(psi)定义)

    设( (m, n)=1,) 则mn的任一因子可唯一写成d (=d 1 d 2) 其中 (d 1|m, d 2| n),根据结论(2)

    [psi(m n)=sum_{d mid m n} heta(d)=sum_{d_1|mwedge d_2| n} heta(d_1 d_2)=sum_{d_1 mid m} sum_{d_2 mid n} heta(d 1 d 2)=sum_{d 1 mid m} sum_{d 2 mid n} heta(d_1) heta(d_2)= psi(m)*psi(n) ]

    (5)(psi(n)=left(1+ heta(p 1)+ hetaleft(p 1^{2} ight)+ldots ight)left(1+ heta(p 2)+ hetaleft(p 2^{2} ight)+. . ight) ldots)

    该结论由3和可乘函数性质易得。

    (6)若 ( heta) 可乘, (mu heta) 可乘, (sum_{d mid n} mu(d) heta(d)) 也可乘(根据性质4),易得

    [sum_{d mid n} mu(d) heta(d)=(1- heta(p 1))(1- heta(p 2)) ldots .(1- heta(p k)) ]

    特别地,

    [egin{alignat}{1} heta(d) &= 1, sum_{d mid n} mu(d) heta(d)=left{egin{array}{ll}1 & n=1 \ 0 & ext { otherwise }end{array} ight. \ heta(d)&=frac{1}{d}, sum_{d mid n} mu(d) heta(d)=left(1-frac{1}{p 1} ight)left(1-frac{1}{p 2} ight) ldotsend{alignat} ]

    (7)【莫比乌斯反演定理】

    F(n)与f(n)均为数论函数

    [若满足F(n)=sum_{d mid n} f(d),则有f(n)=sum_{d mid n} mu(d) Fleft(frac{n}{d} ight)(形式一) \若满足F(n)=sum_{n mid d} f(d),则有f(n)=sum_{n mid d} mu(frac{d}{n}) Fleft(d ight) (形式二) ]

    证明:

    (left(left{egin{array}{l}d mid n \ d 1 mid frac{n}{d}end{array} Leftrightarrowleft{egin{array}{l}d mid frac{n}{d 1} \ d 1 mid nend{array} ight) ight. ight.)

    [egin{alignat}{1}sum_{d mid n} mu(d) Fleft(frac{n}{d} ight)&=sum_{d mid n} mu(d) sum_{d_1 mid frac{n}{d}} f(d 1) \ &=sum_{d mid frac{n}{d 1}} mu(d) sum_{d 1 mid n} f(d 1) \&=sum_{d 1 mid n} f(d 1) sum_{d mid frac{n}{d 1}} mu(d)end{alignat} ]

    根据性质(6)中( heta=1)的情况,只有n=1时函数能取到1,因此只有(frac{n}{d_1}=1)时该项对答案有贡献,即(d_1=n),因此(sum_{d 1 mid n} f(d 1) sum_{d mid frac{n}{d 1}} mu(d)=f(n)),形式一证毕

    例题

    POJ3904 Sky Code

    题意:

    给出n个数,问有多少4元组的gcd为1

    思路:

    直接求很困难,需要容斥,考虑进行转换:

    设F(n):有多少四元组满足gcd为n的整数倍

    f(n):有多少四元组满足gcd为n

    F与f满足第二类反演条件(F(n)=sum_{n mid d} f(d))

    F(n)很好求,只要将包含因数n的数个数num预处理出来即可,(F(n)=C(num,4))

    答案即为f(1),可以通过反演第二种形式转换为求F(n),即

    [f(1)=mu(1) F(1)+mu(2) F(2)+ldots ldots+mu(max n) F(max n)​ ]

    代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    using namespace std;
    typedef long long ll;
    const int maxn=1e4+5;
    const int maxp=1e4+5;
    bool isPrime[maxn];
    int Prime[maxp], primecnt = 0;
    int mu[maxn];
    void GetPrime(int n){//筛到n
    	memset(isPrime, 1, sizeof(isPrime));
    	isPrime[1] = 0;//1不是素数
    	mu[1]=1;
    	for(int i = 2; i <= n; i++){
    		if(isPrime[i])//没筛掉
    			Prime[++primecnt] = i, mu[i]=-1; //i成为下一个素数
    		for(int j = 1; j <= primecnt && i*Prime[j] <= n; j++) {
    			isPrime[i*Prime[j]] = 0;
    			if(i % Prime[j] == 0){//i中也含有Prime[j]这个因子
    			    mu[i*Prime[j]]=0;
    				break;
    			}
                else{
                    mu[i*Prime[j]]=-mu[i];
                }
    		}
    	}
    }
    ll Cn4(ll n){
        if(n<=3){
            return 0;
        }
        else{
            return n*(n-1)*(n-2)*(n-3)/24;
        }
    }
    int a[maxn];
    int tot[maxn];
    void init(int n){
        memset(tot,0,sizeof tot);
    }
    int main () {
        const int N=10000;
        GetPrime(N);
        int n;
        while(~scanf("%d",&n)){
            init(n);
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
            }
            for(int i=1;i<=n;i++){
                int right=sqrt(a[i]);
                for(int j=1;j<=right;j++){
                    if(a[i]%j==0){
                        tot[j]++;
                        tot[a[i]/j]++;
                    }
                }
                if(right*right==a[i]){//多统计了一次
                    tot[right]--;
                }
            }
            ll ans=0;
            for(int i=1;i<=maxn-1;i++){
                ans+=(ll)mu[i]*Cn4(tot[i]);
            }
            printf("%lld
    ",ans);
        }
    }
    

    参考与引用:

    https://blog.csdn.net/tomandjake_/article/details/81083051

    https://blog.csdn.net/tomandjake_/article/details/81083703

  • 相关阅读:
    C# Trick
    DotNet Resource
    人员角色权限
    Verticles for Web Application
    Node Addon
    EventBus
    怎么实现员工和工资大数据分析,echarts+js实现
    winform怎么实现财务上凭证录入和打印
    WPF实现大数据分析
    非常经典的面试题,方案很多,一起交流学习
  • 原文地址:https://www.cnblogs.com/ucprer/p/13494717.html
Copyright © 2011-2022 走看看