BZOJ2331: [SCOI2011]地板

Description

lxhgww的小名叫L”,这是因为他总是很喜欢L型的东西。小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板。现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案?

需要注意的是,如下图所示,L型地板的两端长度可以任意变化,但不能长度为0。铺设完成后,客厅里面所有没有柱子的地方都必须铺上地板,但同一个地方不能被铺多次。

Input

输入的第一行包含两个整数,RC,表示客厅的大小。

接着是R行,每行C个字符。’_’表示对应的位置是空的,必须铺地板;’*’表示对应的位置有柱子,不能铺地板。

Output

输出一行,包含一个整数,表示铺满整个客厅的方案数。由于这个数可能很大,只需输出它除以20110520的余数。

Sample Input

2 2

*_

__

Sample Output

1

HINT

R*C<=100

Source

插头DP
反正这种题根本不用思考,只是码农
一直搞不懂网上的题解为什么写法特别清奇
推荐一种写法 最后一位存右插头,1-m位存下插头, 特别好转移,不用考虑左移右移
写Hash的当我没说(这种题为啥要Hash啊不是暴力能随便过吗)
注意开滚动数组优化,我脑抽,强行交了一发700+M的代码
我还脑抽把最后写成了n+m&1纯属脑残
#include <bits/stdc++.h>

using namespace std;

const int MAXN = 101;
const int MAXV = 177147;
const int mod = 20110520;

char ch[MAXN];

int n, m, mp[MAXN][MAXN], dp[2][MAXV], table[MAXN], cur;

inline void upd(int &x, int y) { x += y; if( x >= mod ) x -= mod; }

inline void solve()
{
	dp[ 0 ][ 0 ] = cur = 1;
	for( int i = 1 ; i <= n ; i++ )
		for( int j = 1 ; j <= m ; j++, cur ^= 1 )
		{
			memset( dp[ cur ], 0, sizeof dp[ cur ] );
			for( int k = 0 ; k < table[ m + 1 ] ; k++ ) if( dp[ cur ^ 1 ][ k ] )
			{
				int left = k % 3, up = k / table[ j ] % 3 ;
				if( mp[ i ][ j ] )
				{
					if( !left && !up ) upd( dp[ cur ][ k ], dp[ cur ^ 1 ][ k ] );
					continue; 
				}
				if( j == 1 && left ) continue;
				if( !left )
				{
					if( !up )
						upd( dp[ cur ][ k + 2 + table[ j ] * 2 ], dp[ cur ^ 1 ][ k ] ),
						upd( dp[ cur ][ k + table[ j ] ], dp[ cur ^ 1 ][ k ] ),
						upd( dp[ cur ][ k + 1 ], dp[ cur ^ 1 ][ k ] );
					else if( up == 1 )
						upd( dp[ cur ][ k ], dp[ cur ^ 1 ][ k ] ),
						upd( dp[ cur ][ k - table[ j ] + 2 ], dp[ cur ^ 1 ][ k ] );
					else
						upd( dp[ cur ][ k ], dp[ cur ^ 1 ][ k ] ),
						upd( dp[ cur ][ k - table[ j ] * 2 ], dp[ cur ^ 1 ][ k ] );
				}
				else if( left == 1 )
				{
					if( !up )
						upd( dp[ cur ][ k ], dp[ cur ^ 1 ][ k ] ),
						upd( dp[ cur ][ k + table[ j ] * 2 - 1 ], dp[ cur ^ 1 ][ k ] );
					else if( up == 1 )
						upd( dp[ cur ][ k - 1 - table[ j ] ], dp[ cur ^ 1 ][ k ] );
				}
				else
				{
					if( !up )
						upd( dp[ cur ][ k ], dp[ cur ^ 1 ][ k ] ),
						upd( dp[ cur ][ k - 2 ], dp[ cur ^ 1 ][ k ] );
				}
			}
		}
}

int main()
{
	table[ 0 ] = 1; for( int i = 1 ; i <= 11 ; i++ ) table[ i ] = table[ i - 1 ] * 3;
	scanf( "%d%d", &n, &m );
	for( int i = 1 ; i <= n ; i++ )
	{
		scanf( "%s", ch + 1 );
		for( int j = 1 ; j <= m ; j++ )
			if( n < m ) mp[ j ][ i ] = ch[ j ] == '*';
			else mp[ i ][ j ] = ch[ j ] == '*';
	}
	if( n < m ) swap( n, m );
	solve();
	return printf( "%d\n", dp[ n * m & 1 ][ 0 ] ), 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值