组合数学相关

.   组合数递推公式:

 

                            𝐶_𝑛^𝑚=𝐶_(𝑛−1)^(𝑚−1)+𝐶_(𝑛−1)^𝑚  

                            C(n,m) = C(n-1,m)+C(n-1,m-1);

 

        .   鸽笼原理

 

                 描述:

       如果n个物体被放进m个盒子,那么至少有一个盒子有⌈𝑛/𝑚⌉个物体。 -->意思为向上取整,用floor函数可以实现
               经典案例: 
                          对数列a1,a2,a3,……an,至少存在1≤i<j≤n,使得∑1_(𝑘=𝑖)^𝑗 𝑎_𝑘 能被n整除。 -->即ai+..+aj 能被n整除。
               证明如下:
                          作和函数 S1 = a1;S2 = a1+a2... ;Sn = a1+a2+a3+..+an; 假设这n个数不能被n整除,则余数的域为1--n-1,
                          这就相当于把n-1个数放进n个笼子,肯定有两个余数相等,则肯定有Sj - Si 能被n整除。
 

           .   容斥原理

 

                     几个集合并集的大小=所有单个集合的大小之和-(所有任意)两个的交+(所有任意)三个的交-(所有任意)四个的交……
                    比如𝐴∪𝐵∪𝐶 = 𝐴+𝐵+𝐶−𝐴∩𝐵−𝐴∩𝐶−𝐵∩𝐶+𝐴∩𝐵∩𝐶。

          四.Ramsey定理

   定义 Kn 表示 n 个点的完全图,对于给定的 n 和 m,存在一个最小的正整数 p 使得将 Kp 的边分别染上红 色或蓝色后,一定存在一个 Kn 子图是红色或者一个 Km 子 图是红色,并且对于更大的任意 p 都成立,这样的 p 记作 r(n,m)。

例题

CCPC 2016 Changchun G Instability
给定一个 n 点 m 边的无向图 (n ≤ 50),问有多少点集满足要 么存在三个点两两相连,要么存在三个点两两不相连

思路:

根据Ramsey 定理,可得r(3,3)=6 ,所以6元及以上集合必然有三元完全子图。只需暴力跑一遍5元集以下即可

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=105;
const int mod=1e9+7;
int fac[N],fnv[N];
int n,m;
int rec[10];
bool groud[N][N];
ll ans;
ll C(int x,int y)
{
    if(x<y) return 0;
    return 1ll*fac[x]*fnv[y]%mod*fnv[x-y]%mod;
}
ll quickmod(ll x,ll y)
{
    ll ans=1;
    for(;y;y>>=1)
    {
        if(y&1) ans=ans*x%mod;
        x=x*x%mod;
    }
    return ans;
}
void getf()
{
    fac[0]=1;
    for(int i=1;i<N;i++) fac[i]=1ll*fac[i-1]*i%mod;
    fnv[N-1]=quickmod(fac[N-1],mod-2);
    for(int i=N-2;i>=0;i--) fnv[i]=1ll*fnv[i+1]*(i+1)%mod;
}
void dfs(int x,int y)
{
    if(x==6) return;
    for(int i=y+1;i<=n;i++)
    {
        rec[x]=i;
        if(x>=3)
        {
            bool flag=false;
            for(int p1=1;p1<x-1;p1++)
            {
                for(int p2=p1+1;p2<x;p2++)
                {
                    for(int p3=p2+1;p3<=x;p3++)
                    {
                        if(groud[rec[p1]][rec[p2]] && groud[rec[p2]][rec[p3]]&& groud[rec[p3]][rec[p1]])
                        {
                            ans++;
                            flag=true;
                            break;
                        }
                        else if(!groud[rec[p1]][rec[p2]] && !groud[rec[p2]][rec[p3]]&& !groud[rec[p3]][rec[p1]])
                        {
                            ans++;
                            flag=true;
                            break;
                        }
                    }
                    if(flag) break;
                }
                if(flag) break;
            }
        }
        dfs(x+1,i);
    }
}
int main()
{
    getf();
    int T;
    scanf("%d",&T);
    for(int no=1;no<=T;no++)
    {
        scanf("%d%d",&n,&m);
        memset(groud,false,sizeof(groud));
        int x,y;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&x,&y);
            groud[x][y]=groud[y][x]=true;
        }
        ans=quickmod(2,n);
        for(ll i=0;i<=5;i++)
        {
            ans=(ans-C(n,i)+mod)%mod;
        }
        dfs(1,0);
        printf("Case #%d: %lld\n",no,ans);
    }
    return 0;
}

五.卡特兰数

卡特兰数是一种经典的组合数,经常出现在各种计算中

公式 h(n) = h(0) * h(n-1)+h1h(n-2)+ …+h(n-1)h(0)
一般式 h(n) = C(2n,n)/(n+1)
另类递推公式:C(n)=C(n-1)
((4*n-2)/(n+1));

推导 考虑减掉不符合要求的01序列,建立对应关系

应用

长度为2*n的0 1序列,要求0的个数与1的个数相同,并且满足在任意位置,序列的前缀中0的个数不多于1的个数。

括号匹配

进出栈顺序

在圆上选择2n个点,将这些点成对连接起来,使得所得到的n条线段不相交的方法数

给定N个节点,能构成多少种形状不同的二叉树

六.第一类斯特林

表示将 n 个不同元素构成m个圆排列的数目
公式 s(n , m) = s(n-1 , m-1) + n*s(n-1 , m)
考虑第n个元素放哪
例题 有n个仓库,每个仓库有两把钥匙,共2n把钥匙。同时又有n位官员。问如何放置钥匙使得所有官员都能够打开所有仓库?
将官员分成m组,每组能打开且只能打开本组的仓

七.第二类斯特林数

表示将n个不同的元素拆分成m个集合的方案数
公式 s(n , m) = s(n-1 , m-1) + m*s(n-1 , m)
同理 考虑 第n个元素放哪

posted @ 2020-02-02 21:33  菠萝炸  阅读(318)  评论(0编辑  收藏  举报