排列组合总结+第一第二类strling数//SDNU1011+HDU4372

第一行照例本鸽子精咕咕咕。

 

以下为正文:

一、基础计算

1.基础计算指的是给定一个n,k计算C\binom{m}{n},一般来说直接利用公式计算即可\frac{n!}{m!(n-m)!}

2.但这种情况一般会涉及到爆long long的问题,需要进行mod,众所周知,除法不能进行模运算,所以可用

    ①乘法逆元来做(逆元的博客我还没写,写了再放上来)

    ②另一个公式C\binom{m}{n} = C\binom{m-1}{n-1} + C\binom{m}{n-1},利用数组递推求得,过程中不停取模。

    类似于这道题:SDNU1311,我用②做的。

下附递推公式:

    for(int i = 0; i <= 1000; ++i)
    {
        for(int j = 0; j <= i; ++j)
        {
            if(i == j)
                dp[i][j] = 1;
            else
                dp[i][j] = (dp[i-1][j-1]+dp[i-1][j])%mod;
        }
    }

其实还有很多其他公式,由ryc师哥提供一个文件,我放到网盘里了,永久有效,欢迎自行下载。提取码 77vd

其他:其实做了一段时间水题后,过了刚入坑的萌新阶段后,你会发现基础计算其实少的可怜,所以就涉及到下面的知识了。

 

 

二、第一类斯特林数

1.多用于求p个不同人围k个相同圆桌而坐,要求各桌非空,其不同方案数(环形)

2.①首先我们想到,当第p个人要坐时,有两种可能。

  ② 前p-1个人坐了k-1个桌,自己一个人坐一桌;前p-1个人坐了k桌,他随便坐。(这里需要注意,随便坐是指随便某桌的某人边上,即直接考虑他坐在哪个人旁边即可)。

  ③可得公式dp[n][k] = dp[n-1][k-1]+(n-1)dp[n-1][k],这里乘n-1的原因如括号内解释。

注:其实第一类斯特林数还有好多应用,但我不会(理直气壮),留个坑。

练习题目:HDUOJ4372

思路:

①首先我们要知道:n的环排列的个数与n-1个元素的排列的个数相等。(当n个元素线性排列时为n!,但成环时会重复,ex:12345,54321为同一个环)

②根据题意,我们可以知道,往右往左看,最高的楼都是可以看到的,那么我们以最高楼为分界线,最高楼一组,左边分为f-1组,右边分为b-1组(左看f组,右看b组)。用每组最高元素代表并截取该组(其余元素任意排列),那么组与组中的最高元素一定是单调递增的,且最高元素一定在最左or最右,那么本组的元素线性排列,就相当于整体元素的全排列。

③所以,整体的结果就是先把n-1个元素分成f-1+b-1组(去掉最高元素),之后把组内元素进行环排列(也就是第一类斯特林),乘起来即为结果。

但实际上我还是不明白代码中公式的原因,我是辣鸡。留个坑。

#include <iostream>
#include <cstdio>
#include <algorithm>
#define MOD 1000000007
#define ll  long long
using namespace std;

ll s[2005][2005] = {0};//strling
ll c[2005][2005] = {0};//组合数

void init()
{
    for(int i = 1; i <= 2000; ++i)
        s[i][0] = 0,c[i][0] = 1;
    c[1][1] = 1;
    for(int i = 2; i <= 2000; ++i)
        for(int j = 1; j <= i; ++j)
        {
            c[i][j] = (c[i-1][j-1]+c[i-1][j])%MOD;
        }
    s[0][0] = 0;
    s[1][1] = 1;
    for(int i = 2; i <= 2000; ++i)
    {
        for(int j = 1; j <= i; ++j)
        {
            s[i][j] = (s[i-1][j-1]+(i-1)*s[i-1][j])%MOD;
        }
    }
}
int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        int f,b;
        ll ans;
        scanf("%d%d%d",&n,&f,&b);
        if(f+b-2 <= 2000)
            ans = (c[f+b-2][f-1]*s[n-1][f+b-2])%MOD;
        else
            ans = 0%MOD;//防止re,WA了n次都因为这个
        printf("%lld\n",ans%MOD);
    }
    return 0;
}

 

 

三、第二类斯特林数

1.多用于求n个有区别小球,要放进m个相同盒子里,且每个盒子非空的方案数

2.同样,最后一个球,有两种情况:前n-1个球放到了m-1个盒中它自己一个盒,或者前n-1个球放到了m个盒中,它随便放。

我觉得这个比第一类好理解,算了,觉得难理解大概因为我是个辣鸡

题目练习:SDNU1011

但是这个题要注意的是,因为题目中盒子是不同的,所以需要在最后将盒子进行全排列,所以为S\left ( m ,n\right )*A_{m}^{m}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long

using namespace std;

ll j(int x)
{
    ll res = 1;
    for(int i = 1; i <= x; ++i)
        res *= i;
    return res;
}

int main()
{
    int n,r;
    ll dp[11][11];
    scanf("%d%d",&n,&r);
    ll a = j(r);
    for(int i = 0; i <= n; ++i)
    {
        for(int j = 0; j <= r; ++j)
        {
            if(i == 0)
            {
                dp[i][j] = 0;
            }
            else
            {
                if(j == 0 || i == 0 || i < j)
                    dp[i][j] = 0;
                else if(j == 1 || i == j)
                    dp[i][j] = 1;
                else
                {
                    dp[i][j] = dp[i-1][j]*j+dp[i-1][j-1];
                }
            }
        }
    }
    printf("%lld",dp[n][r]*a);
    return 0;
}

 

 

现穿插博文无关回忆:

突然想起高二学排列组合的时候。

剑哥在讲台上用一张讲义讲了十几种情况方法,然而我日常上课睡觉划水啥都没学进去。后来学委从电教楼二楼穿过萃英广场,爬到教学楼四楼,花一个大课间的时间给我详细讲各种排列组合。

那时候她停课学数竞,每天都很忙,却还是愿意因为我随口的一句“你能不能教教我排列组合”,而来回好几次(有的大课间我出去不在,有的我在睡觉)(是的我成绩不好都是我自己作的)(但你能相信她甚至拿了张纸备课吗)(我当时特想让她把那张纸留下但没好意思开口)。

她真好啊,那时候也真好啊。

怀念后继续往前走。

 

 

欢迎指出错误qwq

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值