• 容斥(多个集合并问题)


    学习博客:https://blog.csdn.net/m0_37286282/article/details/78869512

    对容斥原理的描述

    容斥原理是一种重要的组合数学方法,可以让你求解任意大小的集合,或者计算复合事件的概率。

    描述

           容斥原理可以描述如下:

             要计算几个集合并集的大小,我们要先将所有单个集合的大小计算出来,然后减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分,依此类推,一直计算到所有集合相交的部分。

    关于集合的原理公式

          上述描述的公式形式可以表示如下:
           

            

          它可以写得更简洁一些,我们将B作为所有Ai的集合,那么容斥原理就变成了:

            

             这个公式是由 De Moivre (Abraham de Moivre)提出的。

    关于维恩图的原理

           用维恩图来表示集合A、B和C:

           

             那么的面积就是集合A、B、C各自面积之和减去 ,  的面积,再加上的面积。

             由此,我们也可以解决n个集合求并的问题。

    关于概率论的原理

           设事件 代表发生某些事件的概率(即发生其中至少一个事件的概率),则:

      

             这个公式也可以用B代表Ai的集合:

    容斥原理的证明

           我们要证明下面的等式:

           

             其中B代表全部Ai的集合

             我们需要证明在Ai集合中的任意元素,都由右边的算式被正好加上了一次(注意如果是不在Ai集合中的元素,是不会出现在右边的算式中的)。

             假设有一任意元素在k个Ai集合中(k>=1),我们来验证这个元素正好被加了一次:

             当size(C)=1时,元素x被加了k次。

             当size(C)=2时,元素x被减了C(2,k)次,因为在k个集合中选择2个,其中都包含x。

             当size(C)=3时,元素x被加了C(3,k)次。

             ……

             当size(C)=k时,元素x被加/减了C(k,k)次,符号由sign(-1)^(k-1)决定。

             当size(C)>k时,元素x不被考虑。

             然后我们来计算所有组合数的和。

             

             由二项式定理,我们可以将它变成:

        

             我们把x取为1,这时表示1-T(其中T为x被加的总次数),所以,证明完毕。

    对于实际问题的应用

           容斥原理的理论需要通过例子才能很好的理解。

             首先,我们用三个简单的例子来阐释这个理论。然后会讨论一些复杂问题,试看如何用容斥原理来解决它们。

             其中的“寻找路径数”是一个特殊的例子,它反映了容斥问题有时可以在多项式级复杂度内解决,不一定需要指数级。

    一个简单的排列问题

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

             我们可以来计算它的逆问题,即第一个元素<=1或者最后一个元素>=8的情况。

             我们设第一个元素<=1时有X组排列,最后一个元素>=8时有Y组排列。那么通过容斥原理来解决就可以写成:

           

             经过简单的组合运算,我们得到了结果:

             

             然后被总的排列数10!减,就是最终的答案了。

    (0,1,2)序列问题

           长度为n的由数字0,1,2组成的序列,要求每个数字至少出现1次,这样的序列有多少种?

             同样的,我们转向它的逆问题。也就是不出现这些数字的序列 不出现其中某些数字的序列。

             我们定义Ai(i=0…2)表示不出现数字i的序列数,那么由容斥原理,我们得到该逆问题的结果为:

               可以发现每个Ai的值都为2^n(因为这些序列中只能包含两种数字)。而所有的两两组合都为1(它们只包含1种数字)。最后,三个集合的交集为0。(因为它不包含数字,所以不存在)

            要记得我们解决的是它的逆问题,所以要用总数减掉,得到最终结果:

             

    方程整数解问题

           给出一个方程:

           

             其中

            求这个方程的整数解有多少组。

            我们先不去理会xi<=8的条件,来考虑所有正整数解的情况。这个很容易用组合数来求解,我们要把20个元素分成6组,也就是添加5块“夹板”,然后在25个位置中找5块“夹板”的位置。

             

             然后通过容斥原理来讨论它的逆问题,也就是x>=9时的解。

             我们定义Ak为xk>=9并且其他xi>=0时的集合,同样我们用上面的添加“夹板”法来计算Ak的大小,因为有9个位置已经被xk所利用了,所以:

             

             然后计算两个这样的集合Ak、Ap的交集:

             

             因为所有x的和不能超过20,所以三个或三个以上这样的集合时是不能同时出现的,它们的交集都为0。最后我们用总数剪掉用容斥原理所求逆问题的答案,就得到了最终结果:

             

    求指定区间内与n互素的数的个数:

           给出整数n和r。求区间[1;r]中与n互素的数的个数。

             去解决它的逆问题,求不与n互素的数的个数。

             考虑n的所有素因子pi(i=1…k)

             在[1;r]中有多少数能被pi整除呢?它就是:

           

             然而,如果我们单纯将所有结果相加,会得到错误答案。有些数可能被统计多次(被好几个素因子整除)。所以,我们要运用容斥原理来解决。

             我们可以用2^k的算法求出所有的pi组合,然后计算每种组合的pi乘积,通过容斥原理来对结果进行加减处理。

             关于此问题的最终实现:

    #include<iostream>
    #include<vector>
    using namespace std;
    void solve(int n,int r)
    {
        vector<int> p;
        for(int i=2;i*i<=n;i++)//求n的所有质因子
        {
            if(n%i==0)
            {
                p.push_back(i);
                while(n%i==0)
                {
                    n/=i;
                }
            }
        }
        if(n>1) p.push_back(n);
    
        //容斥
    //    cout<<"*&"<<endl;
        int ans=0;
        int len=p.size();
    //    cout<<len<<" "<<endl;
        for(int i=1;i<(1<<len);i++)//二进制枚举
        {
    //        cout<<"*"<<endl;
            int mul=1,cnt=0;
            for(int j=0;j<len;j++)
            {
    //            cout<<"*"<<endl;
                if(i&(1<<j))
                {
                    cnt++;
                    mul*=p[j];
                }
            }
            int sum=r/mul;
            if(cnt&1) ans+=sum;
            else ans-=sum;
        }
        cout<<r-ans<<endl;
    }
    int main()
    {
        int n,r;
        cin>>n>>r;
        solve(n,r);
        return 0;
    }
    当初的梦想实现了吗,事到如今只好放弃吗~
  • 相关阅读:
    Android开发必看知识
    Android 网络通信框架Volley(三)
    Android 网络通信框架Volley(二)
    Android 网络通信框架Volley(一)
    AtomicInteger 一个提供原子操作的Integer类
    Android中px dpi dip density densityDpi 的相关说明
    Android键盘映射
    四十一、Git简介
    四十、git config基本篇
    三十九、git add详解
  • 原文地址:https://www.cnblogs.com/caijiaming/p/10745074.html
Copyright © 2020-2023  润新知