[洛谷P3158] [CQOI2011]放棋子


洛谷題目鏈接:[CQOI2011]放棋子

題目描述

在一個m行n列的棋盤里放一些彩色的棋子,使得每個格子最多放一個棋子,且不同

顏色的棋子不能在同一行或者同一列。有多少祌方法?例如,n=m=3,有兩個白棋子和一

個灰棋子,下面左邊兩祌方法都是合法的,但右邊兩祌都是非法的。

輸入輸出格式

輸入格式:

輸入第一行為兩個整數n, m, c,即行數、列數和棋子的顏色數。第二行包含c個正整數,即每個顏色的棋子數。所有顏色的棋子總數保證不超過nm。

輸出格式:

輸出僅一行,即方案總數除以 1,000,000,009的余數。

輸入輸出樣例

輸入樣例#1:

4 2 2
3 1

輸出樣例#1:

8

說明

N,M<=30 C<=10 總棋子數<=250

題解: 一道\(DP\)的好題.

定義狀態\(f[i][j][k]\)表示用前\(k\)種顏色占領了任意\(i\)\(j\)列.

\(a[k]\)表示第\(k\)種顏色的棋子的個數.

轉移很顯然是$$f[i][j][k]=\sum_{l=0}^{i-1}\sum_{r=0}^{j-1}f[l][r][k-1]C_{n-l}^{i-l}C_{m-r}^{j-r}*用a[k]個棋子占領任意i-l行j-r列的方案數$$

那么我們再定義狀態\(g[i][j][k]\)表示用\(k\)個相同顏色的棋子占領任意\(i\)\(j\)列的方案數,直接算不太好算,我們可以考慮容斥:$$g[i][j][k]=C_{ij}^k-\sum_{l=0}^{i}\sum_{r=0}^{j}g[l][r][k]C_{i}^{l}*C_{j}^{r},(l \not= i \ ||\ r \not= j)$$

預處理了\(g\)數組,就可以對\(f\)數組轉移了:$$f[i][j][k]=\sum_{l=0}^{i-1}\sum_{r=0}^{j-1}f[l][r][k-1]C_{n-l}^{i-l}C_{m-r}^{j-r}*g[i-l][j-r][a[k]]$$

因為不一定要放滿整個棋盤,所以$$ans=\sum_{l=1}^n\sum_{r=1}^mf[i][j][c]$$

其實還可以用滾動數組滾掉\(g\)數組的最后一維.

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int N = 30+5;
const int COL = 10+5;
const int K = 250+5;
const int mod = 1e9+9;

int n, m, C, a[N], c[1000][1000], ans = 0;
int f[N][N][COL], g[N][N][1000];
// f : k types of col occupied any i lines, j rows
// g : same type k chess piece occupied any i lines, j rows

int main(){
    cin >> n >> m >> C;
    for(int i = 1; i <= n; i++) cin >> a[i];
    f[0][0][0] = c[0][0] = 1;
    for(int i = 1; i <= 900; i++){
        c[i][0] = 1;
        for(int j = 1; j <= i; j++) c[i][j] = (c[i-1][j]+c[i-1][j-1])%mod;
    }
    for(int k = 1; k <= C; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++){
                if(a[k] > i*j) continue; int res = 0;
                g[i][j][a[k]] = c[i*j][a[k]];
                for(int l = 1; l <= i; l++)
                    for(int r = 1; r <= j; r++)
                        if(l < i || r < j) (res += 1ll*c[i][l]*c[j][r]%mod*g[l][r][a[k]]%mod) %= mod;
                g[i][j][a[k]] = (g[i][j][a[k]]-res+mod)%mod;
            }
    for(int k = 1; k <= C; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                for(int l = 0; l < i; l++)
                    for(int r = 0; r < j; r++)
                        (f[i][j][k] += 1ll*c[n-l][i-l]*c[m-r][j-r]%mod*g[i-l][j-r][a[k]]%mod*f[l][r][k-1]%mod) %= mod;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++) (ans += f[i][j][C]) %= mod;
    cout << ans << endl;
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM