一、概念
简单点的说,就是先给出一个函数 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 2Sample Output
14
3HINT
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; }