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

    摘自peng-ym的博客

    整除分块

    (sum_{i=1}^n lfloor {frac n i} floor)

    朴素 (O(n)) , 整除分块 (O(sqrt n))

    显然有许多连续的 (lfloor {frac n i} floor) 值是一样的。可以发现,每个块的最后一个数是 (frac n {lfloor frac n i floor })

    for(int l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        ans+=(r-l+1)*(n/l);
    }
    

    有时推出的柿子不一定是裸的板子,可能与一些积性函数相乘( (mu),(phi)...),

    此时需要对这些函数统计前缀和,这样跳过一个区间时就乘上一个区间的函数值。(?)

    积性函数&狄利克雷卷积

    %George1123

    以后我也会卷啦!

    积性函数:

    定义:

    如果 (gcd(x,y)=1) 并且 (f(xy)=f(x)·f(y))(f(n)) 是积性函数。

    特别的,对于任意 (x,y) 满足 (f(xy)=f(x)·f(y)) 的是完全积性函数。

    e.g.

    完全积性

    • 一元函数:(epsilon (x)=[x=1]) 是积性函数的单位元(可以随便卷在一起和去掉)

    • 常数函数:(1(x)=1) 好像也有用字母 (I) 的。

    • 单位函数:(id(x)=x)

    积性

    • 欧拉函数:(phi(x)=sum_{i=1}^x [gcd(x,i)=1])

    • 约数个数函数:(d(x)=sum_{d|n} 1)

    • 约数和函数: (sigma(n)=sum_{d|n} d)

    • 莫比乌斯函数:……

    性质:

    如果 (f(x)) 是积性函数,那么 (h(x)) 也是:

    [h(x)=f(x^p)\ h(x)=f^p(x) ]

    如果 (f(x))(g(x)) 是积性函数,那么 (h(x)) 也是:

    [h(x)=f(x)·g(x)\ h(x)=sum_{d|x} f(d)·g(frac x d) ]

    狄利克雷卷积

    定义:

    两个数论函数 (f)(g) 的狄利克雷卷积 ((f*g))

    [(f*g)(x)=sum_{d|x} f(d)·g(frac x d) ]

    性质:

    满足交换律和结合律和分配率。自己卷自己是自己。

    莫比乌斯反演

    莫比乌斯函数

    • 符号:(mu) .

    • 定义:

      [ mu(d)=left{ egin{array}{rcl} 1 & & {d=1}\ (-1)^k & & {d=Pi_{i=1}^k p_i 且 p_i 为互异素数时}\ 0 & & {d 的一个质因子次数ge 2}\ end{array} ight. ]

    • 性质:

      1. 对于任意 (nin {N}^+)(mu*1=epsilon (sum_{d|n} mu(d)=[n=1])) 常用性质

      考虑非0的 (mu(d)) 的和。
      (n)(k) 个质因子,有 (2^k) 个因子,有 (C_{k,i}) 个的有 (i) 个质因子,
      (ans=C(k,0)-C(k,1)+C(k,2)-C(k,3)+...=sum_{i=0}^k (-1)^iC(k,i))
      二项式定理: ((x+y)^n=sum C(n,i)x^iy^{(n-i)})
      (ans=(-1+1)^k=0^k)
      得证。

      1. 对于任意 (nin N^+),

        [sum_{d|n} frac {mu(d)} d=frac {phi(n)} n ]

        Proof

        [首先有结论sum_{d|n} phi(d)=n(id=phi*I)\ 证明:\ frac 1 n,frac 2 n,frac 3 n,...,frac n n \化简这些分数 分母是 d (d|n)\ 每个分母d对应的分子个数就是 phi(d) ]

        [id=phi*I\ id*mu=phi*I*mu\ id*mu=phi*epsilon\ id*mu=phi\ phi=id*mu\ phi(n)=sum_{d|n} mu(d)*frac n d\ sum_{d|n} phi(d)=n(id=phi*I) ]

    • 代码

      void getmu(int n){
          mu[1]=1;
          for(int i=2;i<=n;i++){
              if(!vis[i]) { p[++cnt]=i; mu[i]=-1; }
              for(int j=1;j<=cnt&&p[j]*i<=n;j++){
                  vis[p[j]*i]=1;
                  if(i%p[j]==0) break;
                  else mu[p[j]*i]=-mu[i];
              }
          }
      }
      

      (O(n))

    莫比乌斯反演

    莫比乌斯反演定理

    (F(n))(f(n)) 是定义在 (N) 上的两个函数,并且满足条件

    [F(n)=sum_{d|n} f(d) ]

    有结论:

    [f(n)=sum_{d|n} mu(d)F(lfloor {frac n d} floor) ]

    Proof:

    [sum_{d|n}mu(d)F(lfloorfrac{n}{d} floor)\=sum_{d|n}mu(d)sum_{i|lfloorfrac{n}{d} floor}f(i)\ =sum_{i|n}f(i)sum_{d|lfloorfrac{n}{i} floor}mu(d)\=f(n) ]

    Proof2:

    噫!好!让我们用狄利克雷卷积来证明一下。

    已知

    [F(n)=sum_{d|n} f(d) ]

    求证

    [f(n)=sum_{d|n} mu(d)·F(frac n d) ]

    [转化一下:\ 已知:\ F(n)=sum_{d|n} f(d)\ F(n)=sum_{d|n} f(d)·1(frac n d)\ F=f*1\ 求证:\ f(n)=sum_{d|n} mu(d)·F(frac n d)\ f(n)=mu *F\ 证明:\ F*mu=f*1*mu=f*epsilon=f\ 比上面那坨好多了qwq ]

    莫比乌斯反演有另一种可能更好用的形式:

    条件:

    [F(n)=sum_{n|d} f(d) ]

    结论:

    [f(n)=sum_{n|d}mu(lfloor frac{d}{n} floor)F(d) ]

    证明:
    (k=frac d n)

    (f(n)=sum_{k=1}^{inf} mu(k)F(nk)=sum_{k=1}^{inf} mu(k) sum_{nk|t} f(t)=sum_{n|t} f(t) sum_{k|frac t n} mu(k))

    当且仅当 (frac t n =1) ,即 (t=n) 时, (sum_{k|frac t n}mu(k)=1)

    (sum_{n|t} f(t) sum_{k|frac t n}mu (k)=f(n))


    $F(n)=sum_{n|d} f(d) $

    (f(n)=sum_{n|d}mu(lfloor frac{d}{n} floor)F(d))

    习题

    LG2257 - YY的GCD

    给定 (N,M) ,求 (1le x le N,1le yle M)(gcd(x,y)) 为质数的 ((x,y)) 有多少对?

    (ans=sum_{x=1}^n sum_{y=1}^m [gcd(x,y)=prime])

    第一道莫反不会做 列完这个柿子就壮烈牺牲了。

    (f(d))(gcd(i,j)=d) 的个数

    (F(n))(gcd(i,j)=n)(n) 的倍数的个数。

    (套路)

    (f(d)=sum_{x=1}^n sum_{y=1}^m [gcd(x,y)=d])

    (F(n)=sum_{n|d} f(d)=lfloor frac N n floor lfloor frac M n floor)

    (f(n)=sum_{n|d}mu(lfloor frac{d}{n} floor)F(d))

    (ans=sum_{pin prime} f(p))

    (ans=sum_{pin prime} sum_{p|d}mu(lfloor frac{d}{p} floor)F(d))

    (ans=sum_{pin prime} sum_{d=1}^{min(frac n p,frac m p)} mu(d)F(dp))

    (ans=sum_{pin prime} sum_{d=1}^{min(frac n p,frac m p)} mu(d)lfloor frac n {dp} floor lfloorfrac m {dp} floor)

    (ans=sum_{T=1}^{min(n,m)} lfloor frac n T floor lfloor frac m T floor imes(sum_{t|T,tin prime} mu(lfloor frac T t floor)))

    最后一坨东西可以预处理 前面那坨整除分块

    void getmu(){
        mu[1]=1;
        for(int i=2;i<=N;i++){
            if(!fl[i]) p[++cnt]=i,mu[i]=-1;
            for(int j=1;j<=cnt&&p[j]*i<=N;j++){
                fl[p[j]*i]=1;
                if(i%p[j]==0) continue;
                mu[i*p[j]]=-mu[i];
            }
        }
        for(int i=1;i<=cnt;i++)
            for(int j=1;p[i]*j<=N;j++)
                f[p[i]*j]+=mu[j];//T=p[i]*j t=p[i]
        for(int i=1;i<=N;i++) sum[i]=sum[i-1]+f[i];
        return;
    }//预处理前面那坨
    long long solve(int a,int b){
        long long ans=0;
        for(int l=1,r;l<=a;l=r+1){
            r=min(a/(a/l),b/(b/l));
            ans+=1ll*(sum[r]-sum[l-1])*(a/l)*(b/l);
        }
        return ans;
    }//预处理后面那坨
    int main(){
        int t; scanf("%d",&t);
        getmu();
        while(t--){
            scanf("%d%d",&n,&m);
            if(n>m) swap(n,m);
            printf("%lld
    ",solve(n,m));
        }
        return 0;
    }
    

    草,题解说入门的不适合做这题(

    LG2522 - [HAOI2011]Problem b

    (n) 次询问,每次询问有多少个数对 ((x,y)) 满足 (ale xle b,cle yle d) 且 $gcd(x,y)=k $ 所有东西都是 (5 imes 10^4)

    草 这好像是前一题的低配啊,这确定没搞反吗。

    [sum_{x=a}^b sum_{y=c}^d [gcd(x,y)=k]\ sum_{x=1}^b sum_{y=1}^d [gcd(x,y)=k]+sum_{x=1}^a sum_{y=1}^c [gcd(x,y)=k]-sum_{x=1}^b sum_{y=1}^c [gcd(x,y)=k]-sum_{x=1}^a sum_{y=1}^d [gcd(x,y)=k]\ 即求\ sum_{x=1}^N sum_{y=1}^M [gcd(x,y)=k]\ 我们从上一题得到:\ 设 f(d) 为 gcd(i,j)=d 的个数\ F(n) 为 gcd(i,j)=n 和 n 的倍数的个数。\ f(d)=sum_{x=1}^n sum_{y=1}^m [gcd(x,y)=d]\ F(n)=sum_{n|d} f(d)=lfloor frac N n floor lfloor frac M n floor\ f(n)=sum_{n|d}mu(lfloor frac{d}{n} floor)F(d)\ f(n)=sum_{n|d}mu(lfloor frac{d}{n} floor)lfloor frac N d floor lfloor frac M d floor\ f(n)=sum_{t=1}^{min(N,M)}mu(t)lfloor frac N {nt} floor lfloor frac M {nt} floor\ 然后求解前面那坨用预处理,后面那坨整除分块。 O(N+Tsqrt n) ]

    void getmu(){
        mu[1]=1;
        for(int i=2;i<=N;i++){
            if(!fl[i]) p[++cnt]=i,mu[i]=-1;
            for(int j=1;j<=cnt&&p[j]*i<=N;j++){
                fl[p[j]*i]=1;
                if(i%p[j]==0) continue;
                mu[i*p[j]]=-mu[i];
            }
        }
        for(int i=1;i<=N;i++) sum[i]=sum[i-1]+mu[i];
        return;
    }
    long long solve(int a,int b,int k){
        if(a>b) swap(a,b);
        long long ans=0;
        for(int l=1,r;l<=a;l=r+1){
            r=min(a/(a/l),b/(b/l));
            ans+=1ll*(sum[r]-sum[l-1])*(a/(1ll*l*k))*(b/(1ll*l*k));
        }
        return ans;
    }
    int main(){
        int t; scanf("%d",&t);
        getmu();
        while(t--){
            int a,b,c,d,k;
            scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
            printf("%lld
    ",solve(b,d,k)-solve(a-1,d,k)-solve(b,c-1,k)+solve(a-1,c-1,k));
        }
        return 0;
    }
    

    显然顺序搞反了。。

    [LG3455 - POI2007]ZAP-Queries

    倒序开题,双倍经验,这是好的。这是前一道题的前置题。是 (a=1,b=1) 的情况。

    [LG3327 - SDOI2015]约数个数和

    (d(x))(x) 的约数个数,给定 (n,m) ,求 (sum_{i=1}^n sum_{j=1}^m d(ij))

    [ans=sum_{i=1}^n sum_{j=1}^m d(ij)=sum_{i=1}^n sum_{j=1}^m sum_{x|i} sum_{y|j} [gcd(x,y)=1]\ 原因:gcd(x,y) 不为1时,x=ad,y=bd,xy=abd^2,在 x=ab,y=d^2时已被筛了一次。\ ans=sum_{i=1}^n sum_{j=1}^m sum_{x|i} sum_{y|j} [gcd(x,y)=1]\ =sum_{x=1}^n sum_{y=1}^m lfloor frac n x floor lfloor frac m y floor [gcd(x,y)=1]\ =sum_{i=1}^n sum_{j=1}^m lfloor frac n i floor lfloor frac m j floor [gcd(i,j)=1]\ 设 f(d)=sum_{i=1}^n sum_{j=1}^m lfloor frac n i floor lfloor frac m j floor [gcd(i,j)=d]\ F(x)=sum_{n|d} f(d)=sum_{i=1}^n sum_{j=1}^m lfloor frac n i floor lfloor frac m j floor [x|gcd(i,j)] =sum_{i=1}^{frac n x} sum_{j=1}^{frac m x} lfloor frac n {ix} floor lfloor frac m {jx} floor\ \ans=f(1)=sum_{d=1}^{min(n,m)}mu(d)F(d)=sum_{x=1}^{min(n,m)} mu(x) sum_{i=1}^{frac n x} sum_{j=1}^{frac m x} lfloor frac n {xi} floor lfloor frac m {xj} floor\ 然后整除分块预处理 s(x)=sum_{i=1}^x lfloor frac x i floor O(1) 询问 F(d)\ 这样可以O(Tsqrt n) ]

    void init(){
        for(int i=1;i<=N;i++){
            for(int l=1,r;l<=i;l=r+1){
                r=i/(i/l);
                s[i]+=1ll*(r-l+1)*(i/l);
            }
        }//预处理s
    
        mu[1]=1;
        for(int i=2;i<=N;i++){
            if(!vis[i]) p[++cnt]=i,mu[i]=-1;
            for(int j=1;j<=cnt&&p[j]*i<=N;j++){
                vis[i*p[j]]=1;
                if(i%p[j]==0) continue;
                mu[i*p[j]]=-mu[i];
            }
        }
        for(int i=1;i<=N;i++) sum[i]=sum[i-1]+mu[i];
        return;
    }
    int main(){
        int t; scanf("%d",&t); 
        init();
        while(t--){
            int n,m; scanf("%d%d",&n,&m);
            if(n>m) swap(n,m);
            ll ans=0;
            for(int l=1,r;l<=n;l=r+1){
                r=min(n/(n/l),m/(m/l));
                ans+=1ll*(sum[r]-sum[l-1])*s[n/l]*s[m/l];
            }//分块求
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    LG1829 [国家集训队]Crash的数字表格 / JZPTAB

    (ans=sum_{i=1}^n sum_{j=1}^m lcm(i,j)) (n,mle 10^7)

    [\ans=sum_{i=1}^n sum_{j=1}^m frac {i imes j} {gcd(i,j)} \=sum_{d=1}^{min(n,m)} (d imessum_{i=1}^{frac n d} sum_{j=1}^{frac m d} [gcd(i,j)=1] imes i imes j) \∵sum_{d|n} mu(d)=[n=1] \∴ ans=sum_{d=1}^{min(n,m)} (d imes sum_{i=1}^{frac n d} sum_{j=1}^{frac m d} (sum_{x|gcd(i,j)} mu(x)) imes i imes j ) \sum_{d=1}^{min(n,m)} d imes sum_{x=1}^{min(frac n d,frac m d)} mu(x) sum_{i=1}^{frac n d} sum_{j=1}^{frac m d} i imes j imes[x|gcd(i,j)] \sum_{d=1}^{min(n,m)} d imes sum_{x=1}^{min(frac n d,frac m d)} mu(x) sum_{xu=1}^{frac n d} sum_{xv=1}^{frac m d} x^2uv \提出x^2 \sum_{d=1}^{min(n,m)} d imes sum_{x=1}^{min(frac n d,frac m d)} mu(x)x^2sum_{xu=1}^{frac n d} sum_{xv=1}^{frac m d} uv \sum_{d=1}^{min(n,m)} d imes sum_{x=1}^{min(frac n d,frac m d)} mu(x)x^2(sum_{u=1}^{frac n {dx}}u) imes (sum_{v=1}^{frac m {dx}} v)\ \sum_{d=1}^{min(n,m)} d imes sum_{x=1}^{min(frac n d,frac m d)} mu(x)x^2 s(frac n {dx}) imes s(frac m {dx})\ \mu(x)x^2 用前缀和优化,枚举d,整除分块求出后面一坨。 \ 积分算出时间复杂度近似 O(n) 草不知道怎么搞的。 ]

    void init(){
        mu[1]=1;
        for(int i=2;i<=N;i++){
            if(!vis[i]){ mu[i]=-1; p[++cnt]=i; }
            for(int j=1;j<=cnt&&p[j]*i<=N;j++){
                vis[p[j]*i]=1;
                if(i%p[j]==0) continue;
                mu[p[j]*i]=-mu[i];
            }
        }
        for(int i=1;i<=N;i++)
            sum[i]=(sum[i-1]+1ll*mu[i]%mod*i%mod*i%mod)%mod;
        return;
    }
    int calc(int x){ return (1ll*x*(x+1)/2)%mod; }
    int main(){
        scanf("%d%d",&n,&m);
        if(n>m) swap(n,m);
        init();
        for(int d=1;d<=n;d++){
            int a=n/d,b=m/d,ret=0;
            for(int l=1,r;l<=a;l=r+1){
                r=min(a/(a/l),b/(b/l));
                ret=(ret+1ll*(sum[r]-sum[l-1])*calc(n/(d*l))%mod*calc(m/(d*l))%mod)%mod;
            }
            ans=(ans+1ll*ret*d%mod)%mod;
        }
        printf("%d
    ",(ans+mod)%mod);
        return 0;
    }
    

    SP5971 LCMSUM - LCM Sum

    (T) 次询问,每次询问给定 (n) ,求 (sum_{i=1}^n lcm(i,n))

    [sum_{i=1}^n frac {i imes n} {gcd(i,n)}\ =nsum_{i=1}^n frac {i} {gcd(i,n)}\ =nsum_{d|n} sum_{i=1}^n frac {i} {d} [gcd(i,n)=d]\ =nsum_{d|n} sum_{i=1}^{frac n d} i·[gcd(i,frac n d)=1]\ sum_{i=1}^{t} i·[gcd(i,t)=1] 表示[1,t]中与t互质的数的和。\ 有结论t>1为 frac {phi(d)d} 2,d=1时为1(草我就不会这个) \ 证明:d>1时,与d互质的数总是成对出现。(i与d互质,则d-i与d互质,和为d,有frac {phi(d)} 2 对。\ ans=nsum_{d|n} frac {phi(frac n d)frac n d} 2\ 设f(d)=sum_{d|n} frac {phi(frac n d)frac n d} 2\ 对于每个 1le dle n 将frac {phi(d)·d} 2加入f(dj) 询问时再乘n \复杂度O(T+nlog n) ]

    void init(){
        phi[1]=1;
        for(int i=2;i<=N;i++){
            if(!vis[i]) phi[i]=i-1,p[++cnt]=i;
            for(int j=1;j<=cnt&&p[j]*i<=N;j++){
                vis[p[j]*i]=1;
                if(i%p[j]==0){
                    phi[p[j]*i]=phi[i]*p[j];
                    break;
                }
                phi[p[j]*i]=phi[i]*(p[j]-1);
            }
        }
        for(int i=1;i<=N;i++){
            ll tmp=1ll*phi[i]*i/2;
            if(i==1) tmp=1;
            for(int j=1;j*i<=N;j++) f[j*i]+=tmp;
        }
        return;
    }
    int main(){
        scanf("%d",&t);
        init();
        while(t--){
            scanf("%d",&n);
            printf("%lld
    ",1ll*f[n]*n);
        }
        return 0;
    }
    

    咕咕咕

    qaqaq
  • 相关阅读:
    973. K Closest Points to Origin
    919. Complete Binary Tree Inserter
    993. Cousins in Binary Tree
    20. Valid Parentheses
    141. Linked List Cycle
    912. Sort an Array
    各种排序方法总结
    509. Fibonacci Number
    374. Guess Number Higher or Lower
    238. Product of Array Except Self java solutions
  • 原文地址:https://www.cnblogs.com/zdsrs060330/p/14093705.html
Copyright © 2011-2022 走看看