现在的位置: 首页 > 综合 > 正文

容斥原理的几个问题总结

2017年05月26日 ⁄ 综合 ⁄ 共 1765字 ⁄ 字号 评论关闭

原文摘自:http://www.cppblog.com/vici/archive/2011/09/05/155103.html

据文章说,原文是俄文的,各种翻译才成为中文。表示膜拜,仰慕。ORZ。就需要这么屌屌哒人。。

容斥原理的具体证明就不在描述了。。

 

这应该是对容斥原理最简单粗暴的解释了。。

我们这篇文章关键解释了几个有关容斥原理的小小问题。

1.一个简单的排列问题。

由0到9的数字组成排列,要求第一个数大于1,最后一个小于8,问,共有多少种排列?

分析:直接求的话,求起来很复杂。那么我们可以求该排列的逆问题。也就是求第一数<=1,或者最后一位数>=8的排列数。

我们假设A为第一个数<=1,B为最后一个数>=8。

那么我们要求的逆问题的排列数就是A + B - (A & B)。(我们定义‘+’为集合的并,&为集合的交集,数学符号和逻辑符号并用,望谅解)。。

为什么药减去A & B呢?因为我们的A,也就是说第一个数<=1中也有满足最后一个数>=8的情况,同样的最后一个数>=8的情况中也有第一个数<=1的,那么我们A+B就会多加一部分第一个数<=1&&最后一个数>=8,减去的就是这一部分。。

C = A + B - (A & B) = 2 * 9! + 2 * 9! - 2 * 2 * 8!。那么这部分就是第一位<=1,或者最后一个>=8的排列数。。

我们用10! - C就是我们要求的,第一个数大于1,最后一个数小于8的排列数。。


2.(0, 1, 2)序列问题。

长度为n,由0,2,1组成序列,每个数最少出现一次,这样的序列有多少种。

分析:我们发现,直接求解的话也是比较复杂的。。那么我们就可以求解他是逆问题。

也就是不全部出现0,1,2的序列会有多少种。。用原文是话就是,不出现某些数的序列的种数。

我们设A0是不出现0的序列的个数,A1是不出现1的序列的个数,A2是不出现2的序列的个数。

A0 + A1 + A2 - (A0 & A1) - (A0 & A2) - (A1 & A2) + (A0 & A1 & A2)。就是我们所要求的逆问题是序列。看下面的维恩图:

画的有点挫(不是有点,是太挫了。。。。 - -)。。

可以看来我们可以用另一个问题来解释了。。这个问题是维恩图与求同时不能被三个数整除的一样了。。其实差不多。。

C = A0 + A1 + A2 - (A0 & A1) - (A0 & A2) - (A1 & A2) + (A0 & A1 & A2) = 2^n + 2^n + 2^n - 1 - 1 - 1 + 0.。这就是序列中不出现某些数序列的种类数。。。

总共是序列众数 3^n - (3 * 2^n - 3 + 0)。。就是我们所求的每个数都最少出现一次的序列数。。

3.方程整数解问题。

给一个方程 x1 + x2 + x3 + x4 + x5 + x6 = 20, 0 <= xi <= 8。。问这样的整数解有多少种。。

4.求指定区间内与n互素的数有多少个。。

给定r,n 求[1,r]内与n互素的个数有多少个?看到这个问题,数论有学一点的童鞋可能会想如果r = n的话,不就是欧拉函数了吗?是的,可惜这个问题的r ,n是不一定会相等的。。

分析:直接求解问题就是比较复杂的。所以我们还是研究这个问题是逆问题。也就是说求gcd(k,n) >= 2,在1 -  n 之间k有多少个 。

那么我们就可以枚举n的素因子来进行求解。。。

Code:小小代码:

//时间复杂度O(sqrt(n))...
int solve(int r, int n)
{
    int p[N], top = 0, ans;
    for(int i = 2; i * i <= n; i ++){
        if(n % i == 0){
            p[top ++] = i;
            while(n % i == 0) n = n / i;
        }
    }
    if(n > 1) p[top ++] = n;
    //枚举子集来进行判断加减,cnt为子集元素个数
    for(int i = 1; i < (i << top); i ++){
        int cnt = 0, tmp = 1;
        for(int j = 0; j < top; j ++){
            cnt ++;
            tmp = tmp * p[j];
        }
    }
    if(cnt % 2) ans += r / tmp;
    else ans -= r / tmp;
    return r - ans;
}

这个问题还是比较有趣的。。。

5.在给定区间内被给定集合中至少一个数整除的个数。。

这个问题恰好和hdu 1796 一样。。详见博客:http://blog.csdn.net/u011394362/article/details/40791385

6.能满足一定数目匹配的字符串个数问题。

7.路径数目问题。

8.素数四元组问题。

给你n个数




抱歉!评论已关闭.