zoukankan      html  css  js  c++  java
  • 【组合数学】

    一.鸽巢原理(抽屉原理)

                  1.定理:若有n个鸽巢, n+1只鸽子, 则至少有一个鸽巢里至少有两只鸽子。

                      2.例题:

                                  记T为一队列,初始时为空, 现有n个总和不超过32的正整数依次入队。如果 无论这些数具体为何值,都能找到一种出队的方 式,使得存在某个时刻队列T中的数之和恰好为9, 那么n的最小值是_________。

                       解析:队列中的数为 ak ,设队列的前缀和为 bk ,若要使数之和恰好为九,就需要找到一个 j 与 i ,使得 bj-bi=9。bi取值范围为1-32,现将这32个数构造为集合{1,10}, {2,11}, …, {8,17}, {18,27}, {19,28},…,{23,32} ,{24},{25},{26},一共17个集合。所以至少需要 18 个数字才能让有两个在同一个集合内,满足题意。

                                所以,答案为 18。

                       3.应用:

                         吃糖果(HDU 1205

                                HOHO,终于从Speakless手上赢走了所有的糖果,是Gardon吃糖 果时有个特殊的癖好,就是不喜欢将一样的糖果放在一起吃,喜 欢先吃一种,下一次吃另一种,这样;可是Gardon不知道是否存 在一种吃糖果的顺序使得他能把所有糖果都吃完?请你写个程序 帮忙计算一下。

                       思路:   

                                         可以用“隔板法”求解。找出最多的一种糖果, 把它的数量N看成N个隔板,隔成N个空间(把每个隔板的右边看成一 个空间);其 他所有糖果的数量为S。

                                  当s<N-1时,无解,因为此时必有两个“隔板”是相邻的,不满足题意,反之,当 s>=N-1 时,成立;

     1 #include<bits/stdc++.h>
     2 typedef long long ll;
     3 using namespace std;
     4 int main()
     5 {
     6     ll t,n,i,j,a,sum=0,maxx;
     7     scanf("%lld",&t);
     8     while(t--)
     9     {
    10         sum=0;
    11         maxx=0;
    12         scanf("%lld",&n);
    13         for(i=0;i<n;i++)
    14         {
    15             scanf("%lld",&a);
    16             sum+=a;
    17             if(maxx<a)
    18             maxx=a;
    19         }
    20      if(maxx<=sum-maxx+1)
    21      printf("Yes
    ");    
    22      else
    23      printf("No
    ");
    24      }
    25      return 0;
    26 }

                推广:

                             鸽巢n个, 鸽子m1+m2+…+mn-n+1只, 其中 m1,m2,…,mn, n都是正整数, 则存在一个 i (i={1,2,3,,,n}) ,使得第 i 个鸽巢的数目不小于 mi

                             否则, 总鸽子数<=(m1-1)+(m2-1)+…+(mn-1) 与总鸽子数为m1+m2+…+mn-n+1矛盾

                             Erdös-Szekeres定理

                                   定理: 在由n2+1个实数构成的序列中, 必然 含有长为n+1的单调(增或减)子序列

                         Ramsey定理

                                          Ramsey定理:对于一个给定的两个整m,n>=2,则一定存在一个 最小整数r,使得用两种颜色(例如红蓝)无论给Kr的每条边如 何染色,总能找到一个红色的Km或者蓝色的Kn。显然,当 p>=r的时候,Kp也满足这个性质。

                                  r可以看做一个有关m,n的二元函数,即r(m,n)。

                                  基本性质:
                                 ①等价性 r(m,n)=r(n,m)
                                  ②r(2,n)=n k2较特殊 只有一条边 最小的kr为Kn
                                  ③r(m,2)=m

                                    例题:
                                                    sum(HDU 5776)

                               题目大意    定一个数列,求是否存在连续子列和为m的倍数,存在输出YES,否则输出NO

               思路:
    预处理前缀和,一旦有两个数模m的值相同,说明中间一部分连续子列可以组成m的倍数。假设sum[1,i]%m=k,sum[1,j]%m=k,则sum[i+1,j]%m=0。

                                           这道题相当于把n个物品放进m个抽屉里,如果n>m,那么一定会出现两个前缀和%m==k,所以n>m直接输出yes。

         

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int vis[50005];
     4 int sum[100005];
     5 int main()
     6 {
     7     int t;
     8     scanf("%d",&t);
     9     while(t--)
    10     {
    11         int n,m;
    12         scanf("%d %d",&n,&m);
    13         memset(sum,0,sizeof(sum));
    14         memset(vis,0,sizeof(vis));
    15         for(int i=1;i<=n;i++)
    16         {
    17             int x;
    18             scanf("%d",&x);
    19             sum[i]=sum[i-1]+x;
    20             vis[sum[i]%m]++;
    21         }
    22         if(vis[0]>=1||n>m)
    23             printf("YES
    ");
    24         else
    25         {
    26             int flag=0;
    27             for(int i=1;i<=m;i++)
    28             {
    29                 if(vis[i]>=2)
    30                 {
    31                     flag=1;
    32                     break;
    33                 }
    34             }
    35             if(flag)
    36                 printf("YES
    ");
    37             else
    38                 printf("NO
    ");
    39         }
    40     }
    41 }

     二.杨辉三角 & 二项式定理

     1.二项式定理

                      

              观察计算我们可以得到:

     2.杨辉三角

                    将上图中的系数拆下来组成一个三角形就是杨辉三角了

    1

    1   1

    1   2   1

    1   3   3   1

    1   4   6   4   1

    ......

    三.容斥原理

           要计算几个集合并集的大小,我们要先将所有单个集合的大小计算出来,然后减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分.........依此类推,一直计算到所有集合相交的部分。(可以理解为就是先把所有单个集合全加一遍然后再去重)。

             Venn 图:

                

             例题:整除(cq11z online judge)      

    Time Limits: 1000 ms Memory Limits: 131072 KB Detailed Limits

    Description

    给出n个数a1,a2……an,求区间[L,R]中有多少个整数不能被其中任何一个数整除。

    Input

    第一行三个正整数,n,L,R。

    第二行n个正整数a1,a2……an

    Output

    一个数,即区间[L,R]中有多少个整数不能被其中任何一个数整除。

    Sample Input

    2 1 1000

    10 15

    Sample Output

    867

    Data Constraint

    对于30%的数据,1<=n<=10,1<=L,R<=1000

    对于100%的数据,1<=n<=18,1<=L,R<=10^9

    思路:容斥 ,由于n比较小,可以直接暴力dfs,看看每次选了几个数,奇数加偶数减统计答案 

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 
     5 ll l,r,ans,t[21];
     6 int n;
     7 
     8 ll gcd(ll a,ll b){
     9     if(b==0) return a;
    10     return gcd(b,a%b);
    11 }
    12 
    13 ll lcm(ll a,ll b){
    14     return (a*b)/gcd(a,b);
    15 } 
    16 
    17 void dfs(int depth,int a,ll b){
    18     if(depth>n){
    19         if(a&1) ans+=r/b-(l-1)/b;
    20         else ans-=r/b-(l-1)/b;
    21         return;
    22     }
    23     dfs(depth+1,a+1,lcm(t[depth],b));
    24     dfs(depth+1,a,b);
    25 }
    26 
    27 int main(){
    28     scanf("%d%lld%lld",&n,&l,&r);
    29     for(int i=1;i<=n;i++) scanf("%lld",&t[i]);
    30     dfs(1,1,1);
    31     printf("%lld",ans);
    32     return 0;
    33 }

     四.卡特兰数

              

                    应用:二叉树计数(cq11z online judge)

                        思路:f[i]表示i个相同结点构成的二叉树数量。 

                               选1个结点作为树根,选j个结点作为 树的左子树,剩余的i-j-1个结点作为树的右子树, 则左子树的形状有f[j]种,右子树的形状有f[i-j-1] 种,j可以从0取到i-1。 

                          可以得到如下递推关系式:

                                f[i]= 1  if i==0 ;

                                        ∑j=0i-1   f[j]*f[i-j-1];

     

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<string>
     6 #include<cmath>
     7 #include<vector>
     8 #include<stack>
     9 #include<queue>
    10 #include<climits>
    11 using namespace std;
    12 long long a[55] = {1};
    13 int main() {
    14     int n;
    15     scanf("%d",&n);
    16     for(int i=1; i<=n; i++)
    17         for(int j=0; j<=i; j++)
    18             a[i] += a[j] * a[i-j-1];
    19     printf("%lld",a[n]);
    20     return 0; 
    21 }

     五.Stirling数      

                    第一类Stirling数

                 定义第一类Stirling数s(n,k):把n个不同的元素分配到k个圆排列里,圆不能为空。问有 多少种分法?

                 第一类Stirling数的递推公式: s(n,k) = s(n-1, k-1) + (n-1) s(n-1,k) ,  1<=k <=n ;

                 第二类Stirling数
                 第二类斯特林数S(n,m)表示的是把n个不同的小球放在m个相同的盒子里,不能有空盒子 ,问有多少中分法  

                     递推:
                     s(n,m)=s(n−1,m−1)+ms(n−1,m) 

                  容斥原理:

                      

                    例题:

                         Count the Buildings 

                            思路:               

                                          由于高的楼会挡住低的楼,所以这些楼首先会被划分成f+b-2个区域(除去中间最高的楼),并且左边有f-1个,右边有b-1个。

                                      对于一个区域(假设在左边),这个区域由若干栋楼组成,并且最高的楼一定在最左边。

                                    那么,由一个区域中的元素组成的任意一个环排列,在这个区域中都有唯一的放法,因为要把最高的元素拉到最左边。

                                    所以,原题被简化为:将n-1个元素形成f+b-2个环排列,并将其中f-1个环放在左边的方法数。

                                       所以答案为:S(n-1,f+b-2)*C(f+b-2,f-1);

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    typedef long long ll;
    const int maxn = 2000+10;
    const int MOD = 1000000007;
    int n,f,b;
    ll s[maxn][maxn];
    ll c[maxn][maxn];
    void init(){
        c[0][0] = 1;
        s[0][0] = 1;
        for(int i = 1; i < maxn; i++){
            c[i][0] = c[i][i] = 1;
            s[i][0] = 0;s[i][i] = 1;
            for(int j = 1; j < i; j++){
                c[i][j] = (c[i-1][j] + c[i-1][j-1])%MOD; 
                s[i][j] = ((i-1)*s[i-1][j]+s[i-1][j-1])%MOD; 
            } 
        }
    }
    int main(){
        int ncase;
        init();
        cin >> ncase;
        while(ncase--){
            scanf("%d%d%d",&n,&f,&b); 
            if(f+b-2>n){
                puts("0");
            }else {
                ll ans = (c[f+b-2][f-1]*s[n-1][b+f-2])%MOD; 
                printf("%d
    ",ans);
            }
        }
        return 0;
    }
  • 相关阅读:
    汇编Ring 3下实现 HOOK API
    软件调试之INT 3讲解
    Delphi逆向
    XoftSpy 4.13的注册算法分析
    反调试技术揭秘
    jmp && call && ret 特权级转移 & 进程调度
    PHP Warning: Module 'modulename' already loaded in Unknown on line 0
    PhpStorm和PHPstudy配置调试参数(Xdebug),问题描述Error. Interpreter is not specified or invalid. Press “Fix” to edit your project configuration.
    php 安装xdebug进行调试(phpstorm)
    Windows下PHP多线程扩展pthreads的安装
  • 原文地址:https://www.cnblogs.com/lirh04/p/12253889.html
Copyright © 2011-2022 走看看