BZOJ3294: [Cqoi2011]放棋子

Description

Input

输入第一行为两个整数nmc,即行数、列数和棋子的颜色数。第二行包含c个正整数,即每个颜色的棋子数。所有颜色的棋子总数保证不超过nm

Output

输出仅一行,即方案总数除以 1,000,000,009的余数。

Sample Input

4 2 2
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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值