CF Round #589 (Div. 2)E. Another Filling the Grid 組合數學dp

題目鏈接: https://codeforces.com/contest/1228/problem/E

題意:

你現在有個 nnn*n 的矩陣,現在要你給每個格子填上一個數字 x(x[1,k])x(x\in[1,k]) ,並且要求每一行以及每一列的最小值是 11 ,問你有多少種填數字的方法。

做法:

只會 O(n3)O(n^3) 的做法,果然還是太菜了。。 O(n2)O(n^2) 的做法表示沒弄懂…先寫下三次方的做法…

dp[i][j]dp[i][j] 合法代表枚舉到第 ii 列時,仍有 jj 行還沒有符合條件的方法種數。

dp[i][j]dp[i][j] 可以從兩種情況轉移過來,① dp[i1][j]dp[i-1][j] 表示沒有加進新的邊,② dp[i][x](x[j+1,n])dp[i][x](x\in[j+1,n]) ,表示有在新的行上增加 11

爲什麼要這樣把兩種情況分開呢,因爲如果有在新的行上增加 11 的話,原來已經滿足條件的行就無所謂是不是一定有 11 了,而如果沒有新增加的話,就必須滿足原來的行中至少有一行有 11 ,這樣才能滿足在這一列的時候列合法的情況。

dp[i][j]+=dp[i1][j](k1)j(knj(k1)nj)dp[i][j]+=dp[i-1][j]*(k-1)^j(k^{n-j}-(k-1)^{n-j}) 表示,未滿足的原來的 jj 行中可以任意取除了 11 以外的值,而滿足條件的 njn-j 行除了不能全部取非 11 值以外任意取。

dp[i][j]+=p=j+1ndp[i1][p]Cppj(k1)jknpdp[i][j]+=\sum_{p=j+1}^{n} dp[i-1][p]*C_{p}^{p-j}*(k-1)^j*k^{n-p} 表示,假設我們從原來的值爲 pp 時轉移過來,要從中選 pjp-j 個填上 11jj 行依舊是可以取任意非 11 的值,除了這 pp 行之外的行可以任意取。(注意這裏是可以任意取,原因上面說過了)。

最後的 dp[n][0]dp[n][0] 就是我們要的答案。

代碼

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=255;
ll dp[maxn][maxn],k,C[maxn][maxn];
ll n,qk[maxn],qk1[maxn];
ll quick(ll a,ll b){
    ll ans=1;
    while(b){
        ans=ans*a%mod;
        a=a*a%mod;
        b/=2;
    }
    return ans;
}
void init(){
    rep(i,0,251) C[i][0]=1;
    rep(i,1,251){
        rep(j,1,i){
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
    }
    qk[0]=qk1[0]=1;
    rep(i,1,251) {
        qk[i]=qk[i-1]*k%mod,qk1[i]=qk1[i-1]*(k-1ll)%mod;
    }
}
int main(){
    scanf("%lld%lld",&n,&k);
    init();
    dp[0][n]=1;
    rep(i,1,n){
        rep(j,0,n){
            dp[i][j]=dp[i-1][j]*qk1[j]%mod*((qk[n-j]-qk1[n-j]+mod)%mod)%mod;
            rep(p,j+1,n){
                dp[i][j]=(dp[i][j]+dp[i-1][p]*C[p][p-j]%mod*qk1[j]%mod*qk[n-p]%mod)%mod;
            }
        }
    }
    printf("%lld\n",dp[n][0]);
    return 0;

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