Codeforces Round #589 (Div. 2) E. Another Filling the Grid (dp + 容斥)

題目鏈接

題意:給你一個n * n 矩形,每個格子可以填一個小於等於k的數,要求每一行每一列都至少有一個1,求方案數。

題解:這題難點在於怎麼設狀態轉移方程,以及怎麼轉移,總之就是很難qwq

  • 設dp[i][j] 表示在第i行,有j列已經有1的方案數
  • 那麼就可以由第 i - 1列轉移,也就是dp[i][j] 可以由 dp[i - 1][p] (1<=p<=j) 轉移而來:分兩種情況
  1. j != p ,那麼我們就要在剩下的 n - p 個位置 選擇 j - p 個位置填 1,剩下的位置填 (2~k)的任意數,這樣就湊齊了j個1
  2. 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]);
}

 

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