莫比乌斯反演

一、概念

简单点的说,就是先给出一个函数 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\n",ans);
    }
    return 0;
}

 

posted @ 2017-10-10 21:08  爱国呐  阅读(257)  评论(0编辑  收藏  举报