【BZOJ2331】[SCOI2011]地板 插头DP

【BZOJ2331】[SCOI2011]地板

Description

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

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

【BZOJ2331】[SCOI2011]地板 插头DP

Input

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

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

Output

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

Sample Input

2 2
*_
__

Sample Output

1

HINT

R*C<=100

题解:我们取R和C中小的那维做状态。显然状态是三维的:对于轮廓线上的每个位置,用0表示无插头,1表示有插头,并且这个L还没有拐弯,2表示有插头,并且L已经拐弯了。然后进行3*3的讨论吧!注意一个拐过弯的插头可以停止,一个没拐过弯的插头可以拐弯。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=177200;
const int P=20110520;
int n,m,k,tag,tot;
char str[110][110];
int dp[2][maxn],bt[20],m3[maxn];
inline void upd(int a) {dp[k][a]+=tag; if(dp[k][a]>=P) dp[k][a]-=P;}
int main()
{
scanf("%d%d",&n,&m);
int i,j,S,T,p,q,x,y;
for(i=1;i<=n;i++) scanf("%s",str[i]+1);
if(n<m)
{
for(i=1;i<=m;i++) for(j=1;j<i;j++) swap(str[i][j],str[j][i]);
swap(n,m);
}
for(i=bt[0]=1;i<=m+1;i++) bt[i]=bt[i-1]*3;
for(tot=bt[m+1],i=1;i<tot;i++) m3[i]=i%3;
dp[0][0]=1;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
k^=1;
memset(dp[k],0,sizeof(dp[k]));
for(S=0;S<tot;S++) if(dp[k^1][S])
{
x=bt[j-1],y=bt[j],p=m3[S/x],q=m3[S/y],tag=dp[k^1][S],T=S-x*p-y*q;
if(str[i][j]=='*')
{
if(!p&&!q) upd(T);
continue;
}
if(!p&&!q)
{
if(i!=n&&j!=m) upd(T+((x+y)<<1));
if(i!=n) upd(T+x);
if(j!=m) upd(T+y);
}
if(!p&&q==1)
{
if(i!=n) upd(T+x);
if(j!=m) upd(T+(y<<1));
}
if(!p&&q==2)
{
if(i!=n) upd(T+(x<<1));
upd(T);
}
if(!q&&p==1)
{
if(j!=m) upd(T+y);
if(i!=n) upd(T+(x<<1));
}
if(!q&&p==2)
{
if(j!=m) upd(T+(y<<1));
upd(T);
}
if(p==1&&q==1) upd(T);
}
}
for(S=tot-1;S>=0;S--) dp[k][S]=(m3[S]>0)?0:dp[k][S/3];
}
printf("%d",dp[k][0]);
return 0;
}//10 10 __________ __________ __________ __________ __________ __________ __________ __________ __________ __________
上一篇:SpringMVC+Thymeleaf如何处理URL中的动态查询参数


下一篇:Linux 定时器应用【转】