排列組合總結+第一第二類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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章