近期学了学数论,来写一波总结吧。
(1)排列组合,比较基础的东西了吧。//只写个概念吧,(逃:
概念:就是从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;从m个不同元素中取出n(n≤m)个元素的所有组合的个数,叫做从m个不同元素中取出n个元素的组合数。//这应该都知道吧
公式://博客园现在这个图片功能,额.......
组合恒等式:
//不知道在博客园怎么弄公式,只好弄成图片上来了
补充:1.Catalan数列,数列前几项为1,2,5,14,52,132,429......,通项公式://写个博客发现学会用word文档写公式了
通过Catalan数列我们可以解决①从平面内(1,1)走到(n,n)且不能经过y =x上方的点的路径数②计算有多少种长度为n的括号序列(合法)③每次可以将一个元素入栈或者栈顶元素出栈的方案数④不同节点的二叉树计数⑤有 2n 个人,他们身高互不相同,他们要成两排,每一排有 n 个人,并且满足每一排必须是从矮到高,且后一排的人要比前一排对应的人要高的方案数
2.挡板法:这个方法也是在组合数学中别叫经典的方法吧。几种常见的情况如下:
①如果有10个糖(n),一共有6个人(m),每个人必须都有一个。对于这种问题问你有多少种方法分糖那么这个就是。
//解释:因为我们要将10个糖分给6个人,所以我们就是要将10个糖分为6份,而且每人至少一个。然后我们要将10个糖分成6份就需要5个挡板,然后我们将这10个糖放成一排,因为10个糖完全相同,所以怎么排都是一样的//也就是说只有一种排法。然后这10个糖会出现9个空档,然后我们就要在这9个空挡中放5个挡板了,又因为这5个挡板无差异,所以怎么放都是无顺序的。所以就有这么多情况喽,就是上面说的。//图片放上来就这么大了,我也很无奈......
②但是如果允许有人可以拿不到糖这种情况怎么办呢??遇到这种情况时,我们可以先当成从每个盒子中拿出一个糖,这样我们现在就有n+m个糖了,然后在用①中的方法即可。这是为什么呢?因为我们先从每个盒子里面拿出一个糖,这样我们放的时候如果盒子里面只有一个糖,因为我们先从盒子里面借了一个糖,所以此盒子中是没有糖的。这样就可以处理好允许有空盒子的情况了//这应该很容易懂的吧。
③如果我们需要在每个盒子中至少再放入其编号个糖的话//其实放几个都无所谓,但为了好理解,所以就放入其编号个吧。(上面还是分糖,下面就往盒子里放糖了,没人看见吧(逃:),出现这种情况的话怎么办呢??//相信你已经有思路了吧。我们就和②一样,②是让有的盒子是空的。而现在我们就先往盒子里放入其编号-1个糖//因为挡板法是至少放一个糖,这我们不是又可以用①中的公式来求了吗。没错,就是这么简单↖(^ω^)↗。
3.鸽巢原理(抽屉原理)概说一下是什么吧:如果n+1个物体放进n个盒子,那么至少有一个盒子里包含两个或者更多的物体。//就这么多....鸽巢原理重要的就是这样思想。相信大家在高中之前就已经接触过这种思想了吧,我也就不多说什么了。
//在网上看到的一则鸽巢原理的故事:已知n+ 1个正整数,它们全都小于或等于2n,证明当中一定有两个数是互质的。匈牙利大数学家厄杜斯(PaulErdous,1913 - 1996) 向当年年仅11岁的波萨 (LouisPósa) 提出这个问题,而小波萨思考了不足半分钟便能给出正确的答案。波萨是这样考虑问题:取n个盒子,在第一个盒子我们放1和2,在第二个盒子我们放3和4,第三个盒子是放5和6,依此类推直到第n个盒子放2n-1和2n这两个数。如果我们在n个盒子里随意抽出n+1个数。我们马上看到一定有一个盒子是被抽空的。因此在这n+1个数中必有两个数是连续数,很明显的连续数是互质的。因此这问题就解决了!这就是利用了鸽巢原理的核心思想。
4.容斥原理:这个东西还是比较重要的吧。
①两个集合的容斥原理:如果要计数的事物有A,B两种,那么,先把A,B两个集合中的元素想加,发现在A和B中。A,B相交的这种情况被多算了,所以我们要减去。就像让我们求两个重叠矩形的面积一样。公式:A∪B=A+B-A∩B。
②三个集合的容斥原理:我们还是将这三个元素集合A,B,C的元素个数相加,然后我们可以容易的发现两两重合的部分被多加一次,三个重复的部分被多算了两次。就如下图:
因此:我们可以很容易的看出来A∪B∪C=A+B+C-(A∩B-A∩B∩C)-(B∩C-A∩B∩C)-(C∩A-A∩B∩C)-2(A∩B∩C)
=A+B+C-A∩B-B∩C-C∩A+A∩B∩C
所以公式就是:A∪B∪C=A+B+C-A∩B-B∩C-C∩A+A∩B∩C。//即为:三个圆内的-重合两次的-重合三次的。
然后我们就可以发现容斥原理就是要计算几个集合合并的大小,我们要先将所有单个集合的大小计算出来,然后减去所有两个集合相交的部分,再加回三个集合相交的部分,再减去所有四个集合相交的部分,以此类推。
所以我们就可以得出容斥原理的性质:集合S不具有性质的物体个数公共如下
//总说就是A的个数如果是计数则加如果是偶数就是减了。
例题:[HAOI2012]容易题,POJ3904水晶密码,8的倍数。
---------------------------------------------------补充的东西先就这么多了吧-----------------------------------------------
接下来我们说说组合数的计算吧。
①加法递推-----O(n²)
就是上面组合数恒等式的第二条
1 int C[1001][1001]//根据实际情况开数组 2 memset(C,0,sizeof(C)) 3 for(int i=0;i<=n;i++) 4 { 5 C[i][0]=1; 6 for(int j=0;j<=i;j++) 7 C[i][j]=C[i-1][j-1]+C[i-1][j]; 8 }
②乘法递推-----O(n)
1 C[0]=1; 2 for(int i=1;i<=n;i++)//K为摸数,n为第n行 3 { 4 C[i]=(C[i-1]*(n-i+1)/i)%K; 5 C[i]%=K; 6 }
组合数也差不多就这么多东西了吧,还有什么的话以后再补充好了。
(2)最大公约数(gcd),最小公倍数(lcm)
哇小学都学过的东西了吧,直接放定理也没有什么不妥是吧。//求最大公约数只需要用欧几里得算法(辗转相除法)
定理:gcd(a,b)=gcd(b,a%b)(a>b且a%b!=0)
补充:gcd(a,b)=gcd(b,a%b)(a>b且a%b!=0)证明
我们先设D=a%b ∴a=k*b+D (k∈N) ∴ D=a-k*b 上面很显然对吧。所以我们再设r=gcd(a,b); 所以我们可以很显然的得到下面的几个结论r|a, r|b(这里r|a表示r可以被a整除)∴显然得r|a-k*b//因为a和b都可以整除r。∴r|D成立 ∴r|a%b也成立 所以呢我们可以发现r=gcd(b,a%b);所以gcd(a,b)=gcd(b,a%b);
1 int gcd(int a,int b) 2 {return b==0?a:gcd(b,a%b);}
当然最小公倍数与最大公约数的关系就是:lcm(a,b)=(a*b)/gcd(a,b)
既然有欧几里得算法那当然也有扩展欧几里得算法了。
(3)扩展欧几里得
因为我们很显然的知道gcd(a,b)表示的是a和b的最大公因数,所以我们可以等到这样一个方程:gcd(a,b)=ax+by;我们也当然可以找到一组解x1,y1。可是我们怎么求呢?这时候就要用到扩展欧几里得了。
设a>b,所以①当b=0时,gcd(a,b)=a,所以显然可以得到x1=1,y1=0;
②当a>b>0时,因为gcd(a,b)=gcd(b,a%b),所以我们也很明显的可以讲方程转换为:bx2+(a%b)y2=gcd(b,a%b);所以我们又可以得到bx2+(a%b)y2=ax1+by1;我们得到这个方程是干什么的呢?肯定是有用的哇(这不是废话吗....)。然后我们又可以将这个方程化为ax1+by1=bx2+(a-[a/b]*b)y2;//这里的[]表示向下取整,就是[4.9]=4这个意思。所以ax1+by1=bx2+ay2-[a/b]*by2;然后我们就可以得到ax1+by1=ay2+b(x2-[a/b]*b);这样结果不是很明了了,x1=y2,y1=(x2-[a/b]*b)。我们可以看出x1和y1的值是基于y2和x2的,我们只需要递归求解就好了,因为在递归的时候一定会有一个b=0情况的解,所以递归可以结束了。
总的来说,扩展欧几里得就是用来求已知a,b,求x,y的一组解来满足ax+by=gcd(a,b);//解一定存在。具体就看下面代码吧
1 void exgcd(int a,int b,int &x,int &y) 2 { 3 if(b==0) 4 { 5 x=1; 6 y=0; 7 return; 8 } 9 exgcd(b,a%b,x,y); 10 int t=x; 11 x=y; 12 y=t-a/b*y; 13 }
例题:bzoj 1477[青蛙的约会],NOI2002[荒岛野人]
补充:扩展欧几里得也差不多就是这了,我们在这里补充一下分解质因数好不好哇。
(4)质因数
质因数这一个东西在数论中也占据着十分重要的地位,那么我们就来谈谈质因数。
分解质因数这个东西我们也就不必多说了吧,我们直接筛法求一下素数然后枚举就好了。
下面我们给出一些结论吧:
①//其中p1,p2....pk是n的质因数,也就是说每一个数都可以由这种形式表达出来
然后由①我们又可以得出n的所有因数的个数为(a1+1)(a2+1)...(ak+1).
然后我们还可以得出n的正约束和为:
这也都是些结论吧。
然后我们看一下这个例题:求m!分解质因数后因子n的个数。这怎么搞呢?如果我们直接相乘的话会超过数据类型的范围的。那么,我们再看看,m!=1*2*3*....*(m-2)*(m-1)*m。这个谁都知道吧...因为我们要求m!分解质因数后因子n的个数,所以我们可以把m!的阶乘化为和n有关的。但这怎么联系上呢?我们想,如果m!里含有因子n,那么m!的阶乘就一定可以表示成m!=(n*2n*3n*...*kn)*other//other表示不含因子n的数的乘积,这应该很容易懂的吧。然后我们又可以把这个式子化为:n^k*(1*2*3...*k)*other=n^k*k!*other。//这也很容易懂吧。又因为kn<=m,所以k的最大值就为m/n。我们从上面的表达式中还可以提取出k个n,我们每次循环除就好了,时间复杂度很可观。
具体代码如下:
1 while(m) 2 { 3 sum+=m/n; 4 m=m/n; 5 }
是不是在疑惑代码为什么如此之短,没错就是这么短。//好好理解下。
(5)欧拉函数
//欧拉函数在数论中也是很常用的一个部分.....
(1)定义:欧拉函数是小于或等于n的数中与n互质的数的数目,用φ(n)表示欧拉函数的值。
计算方法为:φ(x)=x(1-1/p1)*(1-1/p2)*(1-1/p3)*...(1-1/pn)
(2)证明:
①:当n=p^k时(p为质数):
因为对于一个素数p,φ(p)=p-1,又因为n=p^k。所以φ(n)=p^k-p^(k-1),因为除了p的倍数以外,其它的数全和n互质//解释因为小于p^k的正整数个数为p^k-1个,其中和p^k不互质的正整数有{p*1,p*2,p*3....p*(p^(k-1)-1)}一共p^(k-1)-1个,所以φ(n)=p^k-p^(k-1)。
②:p*q的欧拉函数
我们假设两个数p,q互质,以为欧拉函数是积性函数,所以φ(p*q)=φ(p)*φ(q);//记住是积性函数就好了......
③:对于任意正整数的欧拉函数
因为我们知道,任意一个正整数n都可以表示为其素因子的乘积//前面质因数分解哪里提过。
//直接搬过来
所以很显然我们可以根据上面两条得出
//pi就是n的素音字累乘上面的n为一共有多少个素因子
④欧拉函数的递推性质,根据这个我们可以在O(n)的时间内求出欧拉函数
若(n%x==0&&(n/x)%x==0)则有φ(n)=φ(n/x)*x;
若(n%x==0&&(n/x)%x!=0)则有φ(n)=φ(n/x)*(x-1)。
代码如下:
1 void Euler(int x) 2 { 3 memset(check,0,sizeof(check)); 4 int l=0; 5 for(int i=2;i<=x;i++) 6 { 7 if(!check[i]) 8 { 9 prime[++l]=i; 10 phi[i]=i-1; 11 } 12 for(int j=1;j<=l&&prime[j]*i<=x;j++) 13 { 14 check[i*prime[j]]=1; 15 if(i%prime[j]) 16 phi[i*prime[j]]=phi[i]*(prime[j]-1); 17 else 18 { 19 phi[i*prime[j]]=phi[i]*prime[j]; 20 break; 21 } 22 } 23 } 24 }
//前两天考试,没有往下写,未完待续...