第五关——数论:组合数学

别来无恙 提交于 2020-02-01 23:29:01

20:44:00 你在台上唱着我的创作,布局谋篇像本悲情小说——许嵩《最佳歌手》

我的寒假,我美好的寒假啊啊啊

“其实我还蛮不想写你的,博客,可是没办法啊,谁叫我的寒假不要我了,我就只好要你了,博客”

目录

 

  • 鸽巢原理

  • 鸽巢原理推广

  • 杨辉三角和二项式系数

  • 容斥定理

  • 卡特兰数

  • 斯特林数

那接下来就要来看一下鸽巢原理(抽屉原理)

也不知道发现它的人是不是看着别人鸽子的窝盯半天才发现的,人家鸽子会不好意思的啦!

  • 定义:如果有n+1个鸽子要进n个鸽巢,则至少存在一个鸽巢种包含两个或更多的鸽子。

  • 例题:

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

    由题意可知bi取值范围为1-32,现将这32个数构造为集合{1,10},{2,11}, …, {8,17}, {18,27}, {19,28},…,{23,32} ,{24},{25},{26},这17个集合中的任一个集合不能包含两个或两个以上的 ,否则它们的差为9,故由鸽巢定理可得出,n的最小值为18.
  • 应用:

    在边长为1的正方形内任取5点,则其中至少有2点的距离不超过√2/2

  • 例题:

    吃糖果

    这道题是典型的鸽巢原理,可用鸽巢原理一种简单的推理方法隔板法进行分析,如果S<N-1.把S个糖果放到隔板之间,这N个隔板不够放.必然至少有两个隔板之间没有糖果,由于这两个隔板是同一种糖果,所以无解。反之则有解。

    注意开long long(尽管数据不大)

    #include <cstdio>
    #include<math.h>
    #include<algorithm>
     using namespace std;
    int main()
    {
        int i,j,n,sum,max_,t;
        scanf("%d",&t);
        {
            while(t--)
            {
                sum=max_=0;
                scanf("%d",&n);
                int *a=(int*)malloc(sizeof(int)*n);
                for(i=0;i<n;i++)
                {
                    scanf("%d",&a[i]);
                    max_=max(max_,a[i]);
                }
                
                for(i=0;i<n;i++)
                {
                    if(a[i]!=max_) sum+=a[i];
                    if(sum>=max_-1) break;
                }
                
                if(i==n) printf("No\n"); 
                else printf("Yes\n");
            }
        }
    }

鸽巢原理推广

  • 鸽巢原理的加强形式

定理: 令q1, q2, q3, ...., qn为正整数。如果将 q1, q2, q3, ...., qn - n + 1 个物体放到n个盒子中,则存在一个i,使得第i个盒子至少含有qi个物品 ;

证明: 假设将q1, q2, q3, ...., qn - n + 1个物品分别放到n个盒子里。如果每一个i (i = {1, 2, ..n}),第i个盒子中放少于qi 个物品,则所有盒子所放物品的总数不超过

    (q1 - 1) + (q2 - 1) + ... + (qn - 1) = q1 + q2 + ... + qn - n

显然,比所要放的总数少一个。因此可以确定,对某个i (i = {1, 2, .. n}),第i个盒子至少包含qi个物品。

  • Erdös-Szekeres定理

简单来说呢,就是在由n2+1n2+1个实数构成的序列中,必然含有长为n+1n+1的单调(增或减)子序列。

  • Ramsey定理

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

  2. 表示形式:r可以看做一个有关m,n的二元函数,即r(m,n)。r(3,3)=6.

  3. 性质:

    ①等价性 r(m,n)=r(n,m)

    ②r(2,n)=n k2较特殊 只有一条边 最小的kr为Kn

    ③r(m,2)=m

  4. 数表:

          

例题

问题很显然,6以及6以上的直接统计345的暴力统计就行了  

就是说ans=3的个数+4的个数+5的个数+C(n,6)C(n,7)+…………+C(n,n)=2^n-C(n,0)-C(n,1)-C(n,2)-3不合法的个数-4不合法的个数-5不合法的个数。

#include<bits/stdc++.h>
using namespace std;
const int maxn=55;
const int mod=1000000007;
int T;
int a[maxn][maxn];
int cc=1;
int m,n;
void input(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        a[u][v]=a[v][u]=1;
    }
}
bool ch(int x,int y,int z){
    int t=a[x][y]+a[x][z]+a[y][z];
    if (t==0||t==3)return true;
    return false;
}
bool h(int x,int y,int z,int w){
    return (ch(x,y,z)||ch(x,y,w)||ch(x,z,w)||ch(y,z,w));
}
bool e(int a,int b,int c,int d,int e){
    return (ch(a,b,c)||ch(a,b,d)||ch(a,b,e)||ch(a,c,d)||ch(a,c,e)||ch(a,d,e)||ch(b,c,d)||ch(b,c,e)||ch(b,d,e)||ch(c,d,e));
}
void solve(){
    long long ans=1;
    for (int i=1;i<=n;i++){
        ans = ans*2;
        ans%=mod;
    }
    ans-=n,ans--;
    ans-=n*(n-1)/2;
    ans+=mod;
    ans%=mod;
    for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
    for(int k=j+1;k<=n;k++)
    if(!ch(i,j,k))ans--;
    for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
    for(int k=j+1;k<=n;k++)
    for(int l=k+1;l<=n;l++)
    if(!h(i,j,k,l))ans--;
    for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
    for(int k=j+1;k<=n;k++)
    for(int l=k+1;l<=n;l++)
    for(int p=1+l;p<=n;p++)
    if(!e(i,j,k,l,p))ans--;
    ans+=mod;
    ans%=mod;
    printf("Case #%d: %lld\n",cc++,ans);
}
int main(){
    scanf("%d",&T);
    while (T--){
        memset(a,0,sizeof(a));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            a[u][v]=a[v][u]=1;
        }
        solve();
    } 
    return 0;
}

这道题就是鸽巢原理的运用。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
typedef long long ll;
ll vis[maxn], a[maxn];
int main() {
    std::ios::sync_with_stdio(false);
    ll n, m;
    while(cin>>n>>m){
        if(!n&&!m){
            break;
        }
        ll sum=0,t;
        memset(vis,0,sizeof(vis) );
        for(ll i=1;i<=m;i++)
        cin>>a[i];
        for(ll i=1;i<=m;i++)
        {
            sum+=a[i];
            t=sum%n;
            if(t==0) 
            {
                for(ll j=1;j<i;j++)
                cout<<j<<" ";
                cout<<i<<endl;
                break;
            }
            else if(vis[t])
            {
                for(ll j=vis[t]+1;j<i;j++)
                cout<<j<<" ";
                cout << i << endl;
                break;
            }
            vis[t] = i;
        }
    }
    return 0;
}

这道题用到的是Ramsey定理

#include<bits/stdc++.h>
using namespace std;
int T,n;
int main() {
    scanf("%d",&T);
    while(T--) 
    {
        scanf("%d",&n);
        int a[10][10]={0};
        for(int i=1; i<n; ++i)
        for(int j=i+1; j<=n; ++j) 
        {
            int t;
            scanf("%d",&t);
            if(t&&n<6) a[i][j]=a[j][i]=1;
        }
        if(n>=6)
        {
            puts("Bad Team!");
            continue;
        }
        int f=0;
        for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j)
        for(int k=j+1;k<=n;++k)
        if(a[i][j]&&a[i][k]&&a[j][k])
        {
            f=1;
            break;
        }
        if(f) puts("Bad Team!");
        else puts("Great Team!");
    }
    return 0;
}

22:19:37 如果这失忆变成了洪水,也对,也对。——鬼卞《失眠症》

杨辉三角和二项式系数

杨辉三角小的时候都学过,那么杨辉三角有些什么规律呢,大家也都知道,那么求杨辉三角除了递推打表还有下面这种方法:
每一行从上一行推导而来。如果编程求杨辉三角第n行的数字,可以模拟这个推导过程,逐级递
推,复杂度是O(n2)。不过,若改用数学公式计算,则可以直接得到结果,比用递推快多了
,这个公式就是(1+x)n。
观察(1+x)n的展开:
(1+x)0 = 1
(1+x)1 = 1+x
(1+x)2 = 1+2x+x2
(1+x)3 = 1+3x+3x2+x3
每一行展开的系数刚好对应杨辉三角每一行的数字。也就是说,杨辉三角可以用(1+x)n来定
义和计算。
二项式定理公式:(a+b)n=C0nan+C1nan1b++Cknankbk++Cnnbn(nN)
二项式定理通项:Tk+1=Cknankbk
 
 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!