第一行照例本鴿子精咕咕咕。
以下爲正文:
一、基礎計算
1.基礎計算指的是給定一個n,k計算,一般來說直接利用公式計算即可。
2.但這種情況一般會涉及到爆long long的問題,需要進行mod,衆所周知,除法不能進行模運算,所以可用
①乘法逆元來做(逆元的博客我還沒寫,寫了再放上來)
②另一個公式,利用數組遞推求得,過程中不停取模。
類似於這道題: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
但是這個題要注意的是,因爲題目中盒子是不同的,所以需要在最後將盒子進行全排列,所以爲*
#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