(1)容斥原理 :求在区间[1,m]里有多少个数,与n互质。假设数据不超过int型。
实现过程分为两步:
1, 求出n的质因子 并保存在数组里面;
2, 求出区间[1,m]里面有多少个数与n不互质。
代码:
- #include <cstdio>
- #include <cmath>
- int p[10];//保存质因子 int型n不会超过10个
- int k;//记录质因子个数
- void getp(int n)//求出n的质因子
- {
- int i;
- k = 0;//初始化
- for(i = 2; i*i <= n; i++)
- {
- if(n % i == 0)
- {
- p[k++] = i;//保存质因子
- while(n % i == 0)
- n /= i;
- }
- }
- if(n > 1) p[k++] = n;//本身是质数
- }
- int nop(int m)//求出区间[1,m]里面有多少个数与n不互质
- {
- int top = 0;//队列顶点
- int que[10100];
- int i, j, t;
- que[top++] = -1;//队列数组保存n所有质因子任意不相同组合的乘积
- for(i = 0; i < k; i++)
- {
- t = top;//利于下面计算
- for(j = 0; j < t; j++)
- {
- que[top++] = que[j] * p[i] * (-1);//奇加偶减
- }
- }
- int sum = 0;//统计个数
- for(i = 1; i < top; i++)
- sum += m / que[i];
- return sum;
- }
- int main()
- {
- int n, m;
- while(scanf("%d%d", &n, &m), n||m)//求区间[1,m]内有多少个数与n互质
- {
- getp(n);
- printf("%d\n", m-nop(m));
- }
- return 0;
- }
(2)欧拉函数:说白了,就是指一个数n在[1,n-1]区间有多少个数与它互质(和容斥原理一样的应用)。
比如说,euler[n] = m代表的意思是在区间[1,n-1]里面有m个数与n互质。
一些欧拉函数的性质:
性质一
对于一个质数n,
φ(n)=n−1
。
性质二
若
n=pk
,则
φ(n)=pk−pk−1=(p−1)pk−1
。
性质三
当
gcd(n,m)=1
时,
φ(nm)=φ(n)∗φ(m)
性质四
设
n=pk11∗pk22...∗pkmm
,则
φ(n)=n∗(1−1p1)∗(1−1p2)∗...(1−1pm)
性质五
欧拉定理:对于互质的整数
a,m
有
aφ(m)≡1(modm)
。
性质六
设小于n的所有与n互质的数的和为
Sum
,
Sum=n∗φ(n)2
性质七
首先
p
是个质数。如果
imodp=0
,那么
φ(i∗p)=p∗φ(i)
,否则
φ(i∗p)=φ(i)∗(p-1)
性质八
直接给式子吧…
n=∑d|nφ(d)
现给个实例:求区间[1,100]内所有数的欧拉函数,euler[1]=1。
求欧拉函数 有两个思路:
1, 筛素数打表,用数组记录每个数的欧拉函数(适用于n不是很大的情况,因为数组不能开无限大);
2, 直接求法计算单个欧拉函数,对于有些题目会比较慢(对于很大的n依然可以求解)。
筛素法:
- #include <cstdio>
- #include <cstring>
- #define MAX 100+1
- int eu[MAX];
- void euler()
- {
- int i, j;
- eu[1] = 1;//1的欧拉函数为1 看题目而定
- for(i = 2; i < MAX; i++)
- {
- if(!eu[i])
- {
- for(j = i; j < MAX; j += i)
- {
- if(!eu[j]) eu[j] = j;
- eu[j] = eu[j] * (i-1) / i;
- }
- }
- }
- }
- int main()
- {
- euler();
- for(int i = 1; i < MAX; i++)
- printf("%d\n", eu[i]);
- return 0;
- }
计算单个欧拉函数:
- #include <cstdio>
- #include <cstring>
- #define MAX 100+1
- int euler(int n)//求n的欧拉函数
- {
- int i;
- int eu = n;//欧拉函数
- for(i = 2; i*i <= n; i++)
- {
- if(n % i == 0)//质因子
- {
- eu = eu * (i-1) / i;
- while(n % i == 0)
- n /= i;//避免再次累加
- }
- }
- if(n > 1) eu = eu * (n-1) / n;//本身就是 质数
- return eu;
- }
- int main()
- {
- for(int i = 1; i < MAX; i++)
- printf("%d\n", euler(i));
- return 0;
- }
对于很多题目,容斥原理若和欧拉函数一起使用,或许会增加程序效率。
(3) 抽屉原理: 又称鸽巢原理,指的是n+1个苹果放进n个盒子里面,一定会有一个盒子有两个苹果。
定理: 一个由n个数构成的数列,总能找到若干个连续的数 使它们之和能被n整除。
证明: 对于数列里面的元素a[1],a[2],...... a[n]。我们可以构造一个数组sum[],用sum[ i ]来存储前i个元素之和(包括第i个元素)。
那么sum数组里面所有的元素只有两种情况:(1) 至少存在一个sum[ i ] 能被n整除;(2) 对于所有的sum[ i ] 都不能被n整除 。
情况(1):定理成立。。。
情况(2):首先我们知道sum数组里面有n个元素,又因为它们都不能被n整除,那么我们可以得到以下信息:任意的(sum[i] %n)都非0且结果都在(1到n-1范围里面)
这样的话--> n个结果在 1到n-1 范围内,必然存在两个相等的结果。而这两个相同结果所对应的sum[] 之差 必定能被 n整除。证毕。
对于抽屉原理,可以有以下拓展:(1) 数列里面元素个数只要大于或者等于n也成立 (2) 找到的 若干个数 不是连续的也成立。