您的位置:首页 > 其它

容斥原理 && 欧拉函数 && 抽屉原理

2017-03-08 22:41 134 查看
原文链接:点击打开链接

(1)容斥原理 :重要应用 求出一个数n在区间[1,m]里面有多少个数与它互质。假设数据不超过int型。

 

实现过程分为两步:

1, 求出m的质因子 并保存在数组里面;

2, 求出区间[1,n]里面有多少个数与m不互质。

 

代码:

[cpp] view
plain copy

#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;  

}  

上面的代码实现是很简单的,也是很好理解的。 网上还有DFS版本,位运算版本的以及递归版本的,这里再给个递归的(另外本人理解不太透彻),至于其它两个有兴趣的可以上网查下。

 

递归版本:

 

[cpp] view
plain copy

#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, int t)//求出区间[1,m]里面有多少个数与n不互质   

{  

    int i, sum = 0;  

    for(i = t; i < k; i++)  

    sum += m / p[i] - nop(m/p[i],i+1);  

    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, 0));  

    }  

    return 0;  

}  

 

(2)欧拉函数:说白了,就是指一个数n在[1,n-1]区间有多少个数与它互质(和容斥原理一样的应用)。

比如说,euler
= m代表的意思是在区间[1,n-1]里面有m个数与n互质。

欧拉函数公式:(我们假设n的质因子有x,y) euler
= n * (1-1/x) * (1-1/y)。若有多个继续添上即可。

欧拉函数拓展:小于或等于n的数中(n > 1),与n互质的数的总和为:euler
* n / 2。

现给个实例:求区间[1,100]内所有数的欧拉函数。这里eu[1] = 1。我不知道会不会有一些题目eu[1] = 0。。。注意啊

 

求欧拉函数 有两个思路:

1, 筛素数打表,用数组记录每个数的欧拉函数(适用于n不是很大的情况,因为数组不能开无限大);

2, 直接求法计算单个欧拉函数,对于有些题目会比较慢(对于很大的n依然可以求解)。

 

筛素法:

[cpp] view
plain copy

#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;  

}  

 

计算单个欧拉函数:

 

[cpp] view
plain copy

#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
。我们可以构造一个数组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) 找到的 若干个数 不是连续的也成立。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: