浅谈变换(证明)→反演→莫比乌斯反演→线筛运用
2016-07-11 23:10
495 查看
首先我们先引入基础错排,借用比较有趣的说法↓:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/4226f77056c411acf5ceb2c252f248a1)
那么为何引入这个?
这个错排的计算方式与普通方式不一样,也是为了后面证明反演做铺垫。
那我们先看一下错排的一般计算推导式:
n 个不同元素的一个错排可由下述两个步骤完成:
第一步,“错排” 1 号元素(将 1 号元素排在第 2 至第 n 个位置之一),有 n - 1 种方法。
第二步,“错排”其余 n - 1 个元素,按如下顺序进行。视第一步的结果,若1号元素落在第 k 个位置,第二步就先把 k 号元素“错排”好, k 号元素的不同排法将导致两类不同的情况发生:
1、 k 号元素排在第1个位置,留下的 n - 2 个元素在与它们的编号集相等的位置集上“错排”,有 f(n -2) 种方法;
2、 k 号元素不排第 1 个位置,这时可将第 1 个位置“看成”第 k 个位置(也就是说本来准备放到k位置为元素,可以放到1位置中),于是形成(包括 k 号元素在内的) n - 1 个元素的“错排”,有 f(n - 1) 种方法。据加法原理,完成第二步共有 f(n - 2)+f(n - 1) 种方法。
根据乘法原理, n 个不同元素的错排种数
f(n) = (n-1)[f(n-2)+f(n-1)] (n>2) 。
(至于级数得到的那个推导式,有兴趣可以自己证一下)
接下来,引入3个很普通的公式:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/875291f3c0f095df0150e746e0846dca)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/6b939c7be5f19c50227b7d6c94a70806)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/36c788f5ac01f637674a8b0df56f5214)
然后结合,可以得到:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/15ac0d25696dff86a1d70f8d661caf86)
这样,我们便得到了普通的二项式反演:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/ccbb93bbab9056c22619d688556b9f40)
莫比乌斯反演呢?可以借助上述的形式“简单推”:首先,下面左边两条式子和上面那三个普通的公式有关联,那就是都很普通,然后可以类推出右边的公式:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/7ea95c5b7c2bf3804c829790c262909f)
→→→→
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/28a5669fd252889185d847cc13649e18)
上面都是在讲莫比乌斯反演的推导→为了方便记忆,并没有比较详细地介绍莫比乌斯反演公式的具体内容,
可以参考这篇博文自行了解:http://www.cnblogs.com/Milkor/p/4464515.html
这是种暴力的方法(不是原创的代码)
筛莫比乌斯函数的代码:
那莫比乌斯反演有什么用呢?用处真的很多,只是看你会不会用(这里只给出2例):
1:给T组N,M.依次求出
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/8942c4bd20b80dfe4398b4df7305a048.png)
的值.(N,M<=10^6,T<=10^3)
求解gcd(a,b).把gcd(a,b)当做n.再通过欧拉函数和式。推导过程如下。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/2d1b87dc2af1143cf4d82c4c31aa94ab.png)
第二个等式是用d来看待式子的方法来化简和式的。
之后再穷举d即可。
代码:
#include<stdio.h>
#include<string.h>
#define N 100
bool mark[N+5];
int prime[N+5];
int num;
int euler[N+5];
int Min(int a,int b){return a>b?a:b;}
void Euler()
{
int i,j;
num = 0;
memset(euler,0,sizeof(euler));
memset(prime,0,sizeof(prime));
memset(mark,0,sizeof(mark));
euler[1] = 1; // multiply function
for(i=2;i<=N;i++)
{
if(!mark[i])
{
prime[num++] = i;
euler[i] = i-1;
}
for(j=0;j<num;j++)
{
if(i*prime[j]>N){break;}
mark[i*prime[j]] = 1;
if(i%prime[j]==0)
{
euler[i*prime[j]] = euler[i]*prime[j];
break;
}
else
{
euler[i*prime[j]] = euler[prime[j]]*euler[i];
}
}
}
}
int main()
{
int i;
int M1,M2;
Euler();
for(i=0;i<num;i++)printf("%d ",prime[i]);
printf("\n");
for(i=1;i<=N;i++)printf("%d ",euler[i]);
printf("\n");
M1 = 2;
M2 = 3;
int sum = 0;
int min = Min(M1,M2);
for(i=1;i<=min;i++)
{
sum += euler[i]*(M1/i)*(M2/i);
}
printf("%d\n",sum);
}
2.给T组N,M.依次求出
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/d5d6b4e6ce5c9e79cbd83f1eaee97d06.png)
的值.(N,M<=10^6,T<=10^3)
在证明之前,先证明以下式子。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/346d8d62ba8454e2b5c4d3f1533517a7.png)
问题的解决推导。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/39394d86088428cdef600c91908f3557.png)
第一个等式。lcm(a,b) = ab/gcd(a,b).
第二个等式。令d=gcd(a,b)。
第三个等式。转化为d的视角。(这个手法经常有)。
第四个等式。转化为莫比乌斯函数。
第五个等式。利用上述的等式来转化。注意d和d'
第六个等式。论文中提到的有趣的化简性质。
第七个等式。其实是d = dd'换元。不过有点老奸巨猾啊。干嘛不设个T = dd'。这个我纠结了半天。
之后就是如论文中介绍的。g(d) 为积性函数。线性筛之。
总体上算法还是N的。
(线筛并没有很详细地讲,之后会有更新,上述内容有参考其它文章)
那么为何引入这个?
这个错排的计算方式与普通方式不一样,也是为了后面证明反演做铺垫。
那我们先看一下错排的一般计算推导式:
n 个不同元素的一个错排可由下述两个步骤完成:
第一步,“错排” 1 号元素(将 1 号元素排在第 2 至第 n 个位置之一),有 n - 1 种方法。
第二步,“错排”其余 n - 1 个元素,按如下顺序进行。视第一步的结果,若1号元素落在第 k 个位置,第二步就先把 k 号元素“错排”好, k 号元素的不同排法将导致两类不同的情况发生:
1、 k 号元素排在第1个位置,留下的 n - 2 个元素在与它们的编号集相等的位置集上“错排”,有 f(n -2) 种方法;
2、 k 号元素不排第 1 个位置,这时可将第 1 个位置“看成”第 k 个位置(也就是说本来准备放到k位置为元素,可以放到1位置中),于是形成(包括 k 号元素在内的) n - 1 个元素的“错排”,有 f(n - 1) 种方法。据加法原理,完成第二步共有 f(n - 2)+f(n - 1) 种方法。
根据乘法原理, n 个不同元素的错排种数
f(n) = (n-1)[f(n-2)+f(n-1)] (n>2) 。
(至于级数得到的那个推导式,有兴趣可以自己证一下)
接下来,引入3个很普通的公式:
然后结合,可以得到:
这样,我们便得到了普通的二项式反演:
莫比乌斯反演呢?可以借助上述的形式“简单推”:首先,下面左边两条式子和上面那三个普通的公式有关联,那就是都很普通,然后可以类推出右边的公式:
→→→→
上面都是在讲莫比乌斯反演的推导→为了方便记忆,并没有比较详细地介绍莫比乌斯反演公式的具体内容,
可以参考这篇博文自行了解:http://www.cnblogs.com/Milkor/p/4464515.html
怎么求莫比乌斯反演?
强行筛u
这是种暴力的方法(不是原创的代码)筛莫比乌斯函数的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void mobius() { memset(check, false, sizeof(check)); mu[1] = 1; int tot = 0; for (int i = 2; i <= N; i++) { if (check[i]) { prime[++tot] = i; mu[i] = -1; } for (int j = 1; j <= tot; j++) { if (i * prime[j] > N) break; check[i * prime[j]] = true; if (i % prime[j] == 0) { mu[i * prime[j]] = 0; break; } else mu[i * prime[j]] = -mu[i]; } } } |
1:给T组N,M.依次求出
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/8942c4bd20b80dfe4398b4df7305a048.png)
的值.(N,M<=10^6,T<=10^3)
求解gcd(a,b).把gcd(a,b)当做n.再通过欧拉函数和式。推导过程如下。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/2d1b87dc2af1143cf4d82c4c31aa94ab.png)
第二个等式是用d来看待式子的方法来化简和式的。
之后再穷举d即可。
代码:
#include<stdio.h>
#include<string.h>
#define N 100
bool mark[N+5];
int prime[N+5];
int num;
int euler[N+5];
int Min(int a,int b){return a>b?a:b;}
void Euler()
{
int i,j;
num = 0;
memset(euler,0,sizeof(euler));
memset(prime,0,sizeof(prime));
memset(mark,0,sizeof(mark));
euler[1] = 1; // multiply function
for(i=2;i<=N;i++)
{
if(!mark[i])
{
prime[num++] = i;
euler[i] = i-1;
}
for(j=0;j<num;j++)
{
if(i*prime[j]>N){break;}
mark[i*prime[j]] = 1;
if(i%prime[j]==0)
{
euler[i*prime[j]] = euler[i]*prime[j];
break;
}
else
{
euler[i*prime[j]] = euler[prime[j]]*euler[i];
}
}
}
}
int main()
{
int i;
int M1,M2;
Euler();
for(i=0;i<num;i++)printf("%d ",prime[i]);
printf("\n");
for(i=1;i<=N;i++)printf("%d ",euler[i]);
printf("\n");
M1 = 2;
M2 = 3;
int sum = 0;
int min = Min(M1,M2);
for(i=1;i<=min;i++)
{
sum += euler[i]*(M1/i)*(M2/i);
}
printf("%d\n",sum);
}
2.给T组N,M.依次求出
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/d5d6b4e6ce5c9e79cbd83f1eaee97d06.png)
的值.(N,M<=10^6,T<=10^3)
在证明之前,先证明以下式子。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/346d8d62ba8454e2b5c4d3f1533517a7.png)
问题的解决推导。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201607/39394d86088428cdef600c91908f3557.png)
第一个等式。lcm(a,b) = ab/gcd(a,b).
第二个等式。令d=gcd(a,b)。
第三个等式。转化为d的视角。(这个手法经常有)。
第四个等式。转化为莫比乌斯函数。
第五个等式。利用上述的等式来转化。注意d和d'
第六个等式。论文中提到的有趣的化简性质。
第七个等式。其实是d = dd'换元。不过有点老奸巨猾啊。干嘛不设个T = dd'。这个我纠结了半天。
之后就是如论文中介绍的。g(d) 为积性函数。线性筛之。
总体上算法还是N的。
(线筛并没有很详细地讲,之后会有更新,上述内容有参考其它文章)
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C++的template模板中class与typename关键字的区别分析
- C与C++之间相互调用实例方法讲解