題目鏈接
題意:給你一個n * n 矩形,每個格子可以填一個小於等於k的數,要求每一行每一列都至少有一個1,求方案數。
題解:這題難點在於怎麼設狀態轉移方程,以及怎麼轉移,總之就是很難qwq
- 設dp[i][j] 表示在第i行,有j列已經有1的方案數
- 那麼就可以由第 i - 1列轉移,也就是dp[i][j] 可以由 dp[i - 1][p] (1<=p<=j) 轉移而來:分兩種情況
- j != p ,那麼我們就要在剩下的 n - p 個位置 選擇 j - p 個位置填 1,剩下的位置填 (2~k)的任意數,這樣就湊齊了j個1
- j == p , 此時已經有足夠多的1了,那麼在這j個位置上我們就可以填任意數,但是一定要保證至少要填一個1,因爲要滿足列也要有1,那麼方案數就是k^p - (k - 1)^p。然後剩下的(n - j)個位置填(2~k)的任意數
代碼如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 255, mod = 1e9 + 7;
ll dp[N][N], C[N][N], p1[N], p2[N];
int n, k;
void init(){
p1[0] = p2[0] = C[0][0] = 1;
for(int i=1;i<=250;i++){
C[i][0]=1;//求組合數
p1[i] = (p1[i - 1] * k) % mod;//求k^i
p2[i] = (p2[i - 1] * (k - 1)) % mod;//求(k-1)^i
for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}
int main()
{
scanf("%d%d", &n, &k);
init();
for(int i = 1; i <= n; ++i)//預處理第1行
dp[1][i] = C[n][i] * p2[n - i] % mod;
for(int i = 2; i <= n; ++i)
for(int j = 1; j <= n; ++j)
for(int p = 1; p <= j; ++p){
ll res = C[n - p][j - p] * p2[n - j] % mod * p1[p] % mod;
if(p == j)
res = (p1[p] - p2[p] + mod) % mod * p2[n - p] % mod;
dp[i][j] =(dp[i][j] + res * dp[i - 1][p] % mod) % mod;
}
printf("%lld\n", dp[n][n]);
}