Description
Input
输入第一行为两个整数n, m, c,即行数、列数和棋子的颜色数。第二行包含c个正整数,即每个颜色的棋子数。所有颜色的棋子总数保证不超过nm。
Output
输出仅一行,即方案总数除以 1,000,000,009的余数。
Sample Input
4 2 2
3 1
3 1
Sample Output
8
HINT
N,M<=30 C<=10 总棋子数<=250
Source
DP+容斥
dp[i][j][k]表示第i种颜色用了j行k列的方案数
dp[i][j][k]=C(j*k,a[i])-∑dp[i][j'][k']*C[n][j'
]*C[m][k']
再套一个f[i][j][k]表示前i种颜色用了j行k列的方案数
直接转移就好了
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 9;
int dp[11][31][31], f[11][31][31], C[901][901], n, m, c, a[11], ans;
inline void upd(int &x, int y) { x += y; if( x >= mod ) x -= mod; if( x < 0 ) x += mod; }
inline void init()
{
for( int i = 0 ; i <= n * m ; i++ )
{
C[ i ][ 0 ] = 1;
for( int j = 1 ; j <= i ; j++ )
upd( C[ i ][ j ], C[ i - 1 ][ j - 1 ] ),
upd( C[ i ][ j ], C[ i - 1 ][ j ] );
}
}
int main()
{
scanf( "%d%d%d", &n, &m, &c );
for( int i = 1 ; i <= c ; i++ ) scanf( "%d", &a[ i ] );
init();
for( int i = 1 ; i <= c ; i++ )
for( int j = 1 ; j <= n ; j++ )
for( int k = 1 ; k <= m ; k++ )
{
if( j * k < a[ i ] || max( j, k ) > a[ i ] ) continue;
dp[ i ][ j ][ k ] = C[ j * k ][ a[ i ] ];
for( int x = 1 ; x <= j ; x++ )
for( int y = 1 ; y <= k ; y++ )
if( ( j - x ) || ( k - y ) )
upd( dp[ i ][ j ][ k ], - 1ll * dp[ i ][ x ][ y ] * C[ j ][ x ] % mod * C[ k ][ y ] % mod );
}
f[ 0 ][ 0 ][ 0 ] = 1;
for( int i = 1 ; i <= c ; i++ )
for( int j = 1 ; j <= n ; j++ )
for( int k = 1 ; k <= m ; k++ )
{
if( j * k < a[ i ] ) continue;
for( int x = 0 ; x < j ; x++ )
for( int y = 0 ; y < k ; y++ )
upd( f[ i ][ j ][ k ], 1ll * f[ i - 1 ][ x ][ y ] * dp[ i ][ j - x ][ k - y ] % mod * C[ j ][ x ] % mod * C[ k ][ y ] % mod );
}
for( int i = 1 ; i <= n ; i++ )
for( int j = 1 ; j <= m ; j++ )
upd( ans, 1ll * f[ c ][ i ][ j ] * C[ n ][ i ] % mod * C[ m ][ j ] % mod );
return printf( "%d\n", ans ), 0;
}