bzoj2331 [SCOI2011]地板

2331: [SCOI2011]地板

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 1427  Solved: 601
[Submit][Status][Discuss]

Description

 

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

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

Input

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

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

Output

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

Sample Input

2 2

*_

__

Sample Output

1

HINT

R*C<=100

Source

Day1

分析:非常神奇的一道题.

   这道题很容易看出来用插头dp做. 但是如果用左括号右括号表示插头这道题做不了,没办法转移.

   题目限制了L两端的长度不能为0,说明L一定存在一个拐点. 可以用插头表示L. 如果状态为0,则表示不存在插头;如果状态为1,则表示插头目前是一条直线;如果状态为2,则表示插头已经拐了一个弯,是一个L形了.

   考虑怎么转移. 假设当前枚举到的点是(i,j),令(i-1,j)的右插头为p,(i,j - 1)的下插头为q,(i,j)的下插头为p',(i,j)的右插头为q',分以下几种合法的情况:

   1.p = q = 0,那么可以转移到3个状态:p' = 1,q' = 0;  p' = 0,q' = 1;  p' = q' = 2. 三者的含义很容易看出来.前两个是引出一条线段,最后一个是引出一条折线段“L形”.

   2.p = 0,q = 1. 这种情况下q可以继续往下走,状态不变,p' = 1,q' = 0. 也可以往右拐,状态变成2, p' = 0,q' = 2.

   3.p = 1,q = 0,和第二种情况类似.

   4.p = 0,q = 2. 有两种选择,要么就在此停下,路径结束. 如果在最后一个非障碍点停下就可以统计答案了. 要么继续走,不能拐弯,这个时候p' = 2,q' = 0.

   5.p = 2,q = 0,和第四种情况类似.

   6.p = q = 1.两个插头结合在一起形成了一个L形,(i,j)不存在右插头和下插头,那么p' = q' = 0.

   最后统计答案找状态等于0的.

   两个坑点:1.maxn要开大!  2.n * m ≤ 100,如果m偏大要交换n,m.

   这道题挺好的,插头不仅可以表示左右括号,它作为维护连通性的工具,还可以被赋予别的意义,前提是要能够转移.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int mod = 20110520,maxn = 1000010;
int n,m,a[110][110],now,pre,pow[110],tx,ty;
char s[110];

struct node
{
    int head[maxn],nextt[maxn],sum[maxn],sta[maxn],tot;
    void clear()
    {
        memset(head,-1,sizeof(head));
        tot = 0;
        memset(sum,0,sizeof(sum));
        memset(sta,0,sizeof(sta));
    }
    void push(int x,int v)
    {
        int hashh = x % 50000;
        for (int i = head[hashh]; i >= 0; i = nextt[i])
        {
            if (sta[i] == x)
            {
                sum[i] = (sum[i] + v) % mod;
                return;
            }
        }
        sum[tot] = v;
        sta[tot] = x;
        nextt[tot] = head[hashh];
        head[hashh] = tot++;
    }
} f[2];

int turnleft(int x,int pos)
{
    return x << pow[pos];
}

int get(int x,int pos)
{
    return (x >> pow[pos]) & 3;
}

int del(int x,int i,int j)
{
    return x & (~(3 << pow[i])) & (~(3 << pow[j]));
}

void solve2(int x,int y,int k)
{
    int p = get(f[pre].sta[k],y - 1);
    int q = get(f[pre].sta[k],y);
    int staa = del(f[pre].sta[k],y - 1,y);
    int v = f[pre].sum[k];
    if (!p && !q)
    {
        if (a[x][y] == 0)
        {
            f[now].push(staa,v);
            return;
        }
        if (x < n && a[x + 1][y] == 1)
            f[now].push(staa | turnleft(1,y - 1),v);
        if (y < m && a[x][y + 1] == 1)
            f[now].push(staa | turnleft(1,y),v);
        if (x < n && a[x + 1][y] == 1 && y < m && a[x][y + 1] == 1)
            f[now].push(staa | turnleft(2,y) | turnleft(2,y - 1),v);
    }
    else if (p == 0 && q == 1)
    {
        if (x < n && a[x + 1][y] == 1)
            f[now].push(staa | turnleft(1,y - 1),v);
        if (y < m && a[x][y + 1] == 1)
            f[now].push(staa | turnleft(2,y),v);
    }
    else if (q == 0 && p == 1)
    {
        if (x < n && a[x + 1][y] == 1)
            f[now].push(staa | turnleft(2,y - 1),v);
        if (y < m && a[x][y + 1] == 1)
            f[now].push(staa | turnleft(1,y),v);
    }
    else if (p == 1 && q == 1)
        f[now].push(staa,v);
    else if (p == 0 && q == 2)
    {
        f[now].push(staa,v);
        if (x < n && a[x + 1][y] == 1)
            f[now].push(staa | turnleft(2,y - 1),v);
    }
    else if (p == 2 && q == 0)
    {
        f[now].push(staa,v);
        if (y < m && a[x][y + 1] == 1)
            f[now].push(staa | turnleft(2,y),v);
    }
}

int solve()
{
    now = 0,pre = 1;
    f[0].clear();
    f[0].push(0,1);
    for (int i = 1; i <= n; i++)
    {
        pre = now;
        now ^= 1;
        f[now].clear();
        for (int k = 0; k < f[pre].tot; k++)
            f[now].push(turnleft(f[pre].sta[k],1),f[pre].sum[k]);
        for (int j = 1; j <= m; j++)
        {
            pre = now;
            now ^= 1;
            f[now].clear();
            for (int k = 0; k < f[pre].tot; k++)
                solve2(i,j,k);
        }
    }
    for (int i = 0; i < f[now].tot; i++)
        if (f[now].sta[i] == 0)
            return f[now].sum[i];
    return 0;
}

int main()
{
    freopen("test.txt","r",stdin);
    for (int i = 1; i <= 100; i++)
        pow[i] = i * 2;
    scanf("%d%d",&n,&m);
    if (n >= m)
    {
        for (int i = 1; i <= n; i++)
        {
            scanf("%s",s + 1);
            for (int j = 1; j <= m; j++)
            {
                if (s[j] == '_')
                    a[i][j] = 1;
                else
                    a[i][j] = 0;
            }
        }
    }
    else
    {
        for (int i = 1; i <= n; i++)
        {
            scanf("%s",s + 1);
            for (int j = 1; j <= m; j++)
            {
                if (s[j] == '_')
                    a[j][i] = 1;
                else
                    a[j][i] = 0;
            }
        }
        swap(n,m);
    }
    printf("%d\n",solve() % mod);

    return 0;
}

 

posted @ 2018-02-22 18:21  zbtrs  阅读(201)  评论(0编辑  收藏  举报