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

    一、概念

    简单点的说,就是先给出一个函数 F(n) ,然后再由 F(n)定义一个新函数 G(n)

    其中   G(n) = sigma(F(d)) (其中d被“包含”于n)       PS:这里出现的sigma是累加,后面出现的也同样是累加。

    然后 现在我们不知道 F(n)的值 , 却知道 G(n), 接着我们就可以通过 反演由G(n)反向得到F(n)。

    看下面几个例子更能加深了解

     二、例题

    例1:

    我们直接定义 G(n)=sigma(F(i)) (1<=i<=n)    {这里的每个F(i),相对于G(n)实际上就是一种包含关系}

    然后我们现在已经知道 G(n)=n*(n+1)/2;

    接下来 我们要通过 G(n)反向得到F(n) 的过程,就是反演

    当然,这个问题很简单,很容易都可以看出来 F(n)=n 

     

    例2:

    我们先令S,X 都表示集合 比如 S={1,4,6} X={2} 等  并令|S|表示 S中元素的个数

    接着定义 集合上的函数 F(S)   /*具体怎么定义不用管,我们只需要知道有这么一个关于集合的函数F就好了 ) */ 

    然后再定义 G(S)=sigma(F(X)) (其中X是S的子集)   {这里也是一种包含关系,集合的包含}

    接着我们不知道F(S),想通过G(S) 来得到 F(S)

    我们已经有现成的关于集合包含的莫比乌斯反演公式了 

    那么就可以得出

    F(S)=sigma((-1)^(|S|-|X|) * G(X))  (其中X是S的子集)

      

    例3:(ACM常用)

    先令  d|n 表示 d能整除n  比如 2|4 

    定义 关于 整数 的函数 F(n)

    然后 定义 G(n)=sigma(F(d)) (其中d|n)  

    上面的这种包含关系就更复杂了,只有当d是n的因子的时候,F(d)才会被包含在G(n)中。

     

     

    三、一类反演

    一类反演就是例3中的那一类

    直接给出结论,原式 :  G(n)=sigma(F(d))  (其中d|n)  

    反演公式:   F(n)=sigma(U(n/d)*G(d))   U是一个函数,是每一项 G(d) 的系数

    在证明之前,我们先想一下,为什么反演公式会是   F(n)=sigma(U(n/d)*G(d))   这样的型式?

    依旧通过例题来找规律 

    我们令 n=6;

    那么 在计算 F(6)的时候,我们会用到 G(1) G(2) G(3) G(6)

    我们考察者4个G

    G(1) = F(1)

    G(2) = F(1)+F(2)

    G(3) = F(1)+F(3)

    G(6) = F(1)+F(2)+F(3)+F(6)

    观察上面可以发现 每个 G(n)都是由一些F(d)累加得到的 

    当我们需要逆向有G得到F(n)时,只需要将一些 与 F(n) 有关的 G进行容斥,最终组合得到F(n)!

    比如 F(6) = G(6)-G(2)-G(3)+G(1) 

    这类莫比乌斯反演的实质也就是容斥原理的应用!!

     

    那么我们现在知道为什么 这类反演公式会是 这个形式了,而且对其原理也有了更深的理解,现在该想一想公式的细节了。

    既然我们知道要得到 F(n) ,只需要将与其相关的 G进行容斥就可以,那么剩下的问题就是每个G的系数。

    我们以 求解 F(6)为例子来说明 ,并定义一个系数函数 H(d,n).

    其中 H(d,n)表示 求解F(n)时,G(d)的系数  (其中d|n)

    所以可以得到这个式子 F(6) = H(6,6)*G(6)+H(2,6)*G(2)+H(3,6)*G(3)+H(1,6)*G(1)

    我们用 a,b,c,d分别替代 四个H(6,6),H(2,6),H(3,6),H(1,6),并且把对应的G用F表示出来,得到

    F(6)=a*(F(6)+F(3)+F(2)+F(1))+b*(F(2)+F(1))+c*(F(3)+F(1))+d*F(1),再变形一下,又有

    F(6)*(a-1)+F(3)*(a+c)+F(2)*(a+b)+F(1)*(a+b+c+d)=0,把F(6),F(3),F(2),F(1)当作不同的元,则得到了下面的方程组

    a-1==0

    a+c==0

    a+b==0

    a+b+c+d==0

    由此发现,四个未知数,四个方程,只需要解出方程,就能知道对于G的系数。

    再深入的想一下,对于每个 F(n),假设他的因子数为,m,则通过这种方式,总能设出m个未知数,m个方程,

    这样总能找到解,而这也为莫比乌斯反演的可能性作出了解释。

    四、例题

    Description

    对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

    Input

    第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k

    Output

    共n行,每行一个整数表示满足要求的数对(x,y)的个数

    Sample Input

    2
    2 5 1 5 1
    1 5 1 5 2

    Sample Output

    14
    3

    HINT

    100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <set>
    #include <map>
    #include <string>
    #include <math.h>
    #include <stdlib.h>
    #include <time.h>
    using namespace std;
    const int MAXN = 100000;
    bool check[MAXN+10];
    int prime[MAXN+10];
    int mu[MAXN+10];
    void Moblus()
    {
        memset(check,false,sizeof(check));
        mu[1] = 1;
        int tot = 0;
        for(int i = 2; i <= MAXN; i++)
        {
            if( !check[i] )
            {
                prime[tot++] = i;
                mu[i] = -1;
            }
            for(int j = 0; j < tot; j ++)
            {
                if( i * prime[j] > MAXN) break;
                check[i * prime[j]] = true;
                if( i % prime[j] == 0)
                {
                    mu[i * prime[j]] = 0;
                    break;
                }
                else
                {
                    mu[i * prime[j]] = -mu[i];
                }
            }
        }
    }
    int sum[MAXN+10];
    //找[1,n],[1,m]内互质的数的对数
    long long solve(int n,int m)
    {
        long long ans = 0;
        if(n > m)swap(n,m);
        for(int i = 1, la = 0; i <= n; i = la+1)
        {
            la = min(n/(n/i),m/(m/i));
            ans += (long long)(sum[la] - sum[i-1])*(n/i)*(m/i);
        }
        return ans;
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        Moblus();
        sum[0] = 0;
        for(int i = 1;i <= MAXN;i++)
            sum[i] = sum[i-1] + mu[i];
        int a,b,c,d,k;
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
            long long ans = solve(b/k,d/k) - solve((a-1)/k,d/k) - solve(b/k,(c-1)/k) + solve((a-1)/k,(c-1)/k);
            printf("%lld
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    探究platform_driver中的shutdown用途
    Linux内存调试工具初探-MEMWATCH(转)
    kernel3.13 针对 Vmware安装存在的问题解决
    xgcom linux下的串口助手
    ubuntu 13.10 无法播放 mp3
    ubuntu 13.04添加 flash_plugin
    Linux安装mysql——源码安装
    Ubuntu 12.04中MyEclipse 10.6+下载+安装+破解
    [零基础学JAVA]Java SE面向对象部分.面向对象基础(06)
    [零基础学JAVA]Java SE面向对象部分.面向对象基础(05)
  • 原文地址:https://www.cnblogs.com/aiguona/p/7647822.html
Copyright © 2011-2022 走看看