【SCOI2011】bzoj2331 地板

175 篇文章 0 订阅
115 篇文章 0 订阅

Description

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

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

Input

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

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

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

插头dp。
几个需要注意的地方:
哈希表记录被更新的状态,只用这些状态转移。
总共要保存 c+1 个插头。
行末不能向右接插头。
记录了插头代表“已经插进来”,不能停在旁边的格子。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=20110520,p=100007;
char s[110][110];
int fir[2][100010],ne[2][200010],val[2][200010],ans[2][200010],last[2][100010],
tot,cur,y;
int find(int x)
{
    int y=x%p;
    if (last[cur&1][y]<cur)
    {
        fir[cur&1][y]=0;
        last[cur&1][y]=cur;
    }
    for (int i=fir[cur&1][y];i;i=ne[cur&1][i])
        if (val[cur&1][i]==x) return i;
    tot++;
    ne[cur&1][tot]=fir[cur&1][y];
    fir[cur&1][y]=tot;
    val[cur&1][tot]=x;
    ans[cur&1][tot]=0;
    return tot;
}
void upd(int xx)
{
    int z=find(xx);
    ans[cur&1][z]+=y;
    ans[cur&1][z]%=mod;
}
int main()
{
    int r,c,x,x0,x1;
    scanf("%d%d",&r,&c);
    for (int i=0;i<r;i++) scanf("%s",s[i]);
    if (r<c)
    {
        for (int i=0;i<c;i++)
            for (int j=i+1;j<c;j++)
                swap(s[i][j],s[j][i]);
        swap(r,c);
    }
    cur=0;
    find(0);
    ans[0][1]=1;
    for (int i=0;i<r;i++)
    {
        for (int j=0;j<c;j++)
        {
            cur++;
            tot=0;
            for (int k=0;k<p;k++)
            if (last[cur&1^1][k]==cur-1)
            for (int l=fir[cur&1^1][k];l;l=ne[cur&1^1][l])
            {
                x=val[cur&1^1][l];
                y=ans[cur&1^1][l];
                x0=x&3;
                x1=(x>>2*c)&3;
                if (s[i][j]=='*')
                {
                    if (!(x0&3)&&!(x1&3)) upd(x>>2);
                    continue;
                }
                if (x0&2)
                {
                    if (x1&2) upd((x>>2)^(2<<2*(c-1)));
                    if (!(x1&3))
                    {
                        upd((x>>2)|(2<<2*(c-1)));
                        if (j<c-1) upd((x>>2)|(1<<2*c));
                    }
                }
                if (x0&1)
                {
                    if (!(x1&3))
                    {
                        upd(x>>2);
                        upd((x>>2)|(1<<2*(c-1)));
                    }
                }
                if (!(x0&3))
                {
                    if (!(x1&3))
                    {
                        if (j<c-1) upd((x>>2)|(2<<2*c));
                        if (j<c-1) upd((x>>2)|(1<<2*c)|(1<<2*(c-1)));
                        upd((x>>2)|(2<<2*(c-1)));
                    }
                    if (x1&1)
                    {
                        upd((x>>2)^(1<<2*(c-1)));
                        if (j<c-1) upd((x>>2)^(1<<2*(c-1))|(1<<2*c));
                    }
                    if (x1&2)
                    {
                        if (j<c-1) upd(((x>>2)^(2<<2*(c-1)))|(2<<2*c));
                        upd((x>>2)^(3<<2*(c-1)));
                    }
                }
            }
        }
    }
    printf("%d\n",ans[cur&1][find(0)]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值