BZOJ 2331 [SCOI2011]地板

来源:互联网 发布:网络ssid没有wlan 编辑:程序博客网 时间:2024/04/26 21:37

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2331


题意:给一个n*m的格子,有一些地方可以放置,有一些地方不可以放置,现在只用'L'型的地板铺满整个格子,L型地板的两端长度可以任意变化,但不能长度为0。铺设完成后,可以放置的地方都必须铺上地板,但同一个地方不能被铺多次,求不同的铺法。


思路:插头dp从上到下,左到右逐格递推,所以我们只定义向下和向右两个方向,那么对于任意的L型的格子内部的连通线,在向下和向右的方向上只能是进入到拐点或从拐点内出。我们就用4进制表示轮廓线,0表示此处无连通,1表示(向下/向右方向上)一个入的连通线,2表示(向下/向右方向上)一个出的连通线。因为L型两端的长度至少为1,所以只要保证有一个连通线的长度就可以了,一个连通线跨越两格,除去拐点格,还剩下一格长度。然后就是根据上方和左方的连通性来转移状态。n*m<=100,所以若m>n就翻转地图,让小的成为m。


#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define LL long long#define Clean(x,y) memset(x,y,sizeof(x))int n,m;int pre,cur;char g[100][100];const int Hash = 10007;const int maxn = 2009999;const int mod = 20110520;LL ans;int bit = 3;int inc = 2;int code[30];struct hash_table{    int head[Hash] , next[maxn];    LL state[maxn],value[maxn];    int size;    void clear()    {        size = 0;        Clean(head,-1);    }    void push( LL S , LL V )    {        int index = S % Hash;        for( int k = head[index]; k != -1; k = next[k] )            if ( state[k] == S )            {                value[k] = (value[k] + V) % mod;                return;            }        state[size] = S , value[size] = V;        next[size] = head[index] , head[index] = size++;    }}dp[2];inline void decode( LL S , int m ){    for( int i = 0; i <= m; i++ ) code[i] = S & bit , S >>= inc;}inline LL encode( int m ){    LL ans = 0;    for( int i = m; i >= 0; i-- )    {        ans <<= inc;        ans |= code[i];    }    return ans;}bool check( int m ){    for( int i = 0; i <=m; i++ ) if ( code[i] ) return false;    return true;}void DP( int x , int y , int k ){    decode( dp[pre].state[k] , m );    int left = code[y-1] , up = code[y];    LL V = dp[pre].value[k];    code[y] = code[y-1] = 0;    if ( g[x][y] == '*' ) //当前格子不可放置    {        if ( !left && !up ) dp[cur].push( encode(m) , V ); //左 上 必须无连通才可以转移    }    else    {        if ( !left && !up ) //左、上 无连通        {            //当前格子向下延伸成为下方某个拐点的一条边            if ( x < n && g[x+1][y] == '_' ) code[y-1] = 1 , dp[cur].push( encode(m) , V ) , code[y-1] = 0;            //当前格子向右延伸成为右方某个拐点的一条边            if ( y < m && g[x][y+1] == '_' ) code[y] = 1 , dp[cur].push( encode(m) , V ) , code[y] = 0;            //当前格子可以作为一个拐点格,向下,右延伸。            if ( x < n && y < m && g[x+1][y] == '_' && g[x][y+1] == '_' ) code[y] = 2 , code[y-1] = 2 , dp[cur].push( encode(m) , V );        }        else if ( !left || !up ) //有一个连通        {            if ( up == 1 )//上方作为入连通 , 也就是说下方应该存在一个拐点            {                //继续延伸                if ( x < n && g[x+1][y] == '_' ) code[y-1] = 1 , dp[cur].push( encode(m) , V ) , code[y-1] = 0;                //停止延伸 , 此格子作为拐点 , 并且该向右拐出去                if ( y < m && g[x][y+1] == '_' ) code[y] = 2 , dp[cur].push( encode(m) , V ) , code[y] = 0;            }            else if ( up == 2 ) //作为上方某个拐点的出边            {                //继续出                if ( x < n && g[x+1][y] == '_' ) code[y-1] = 2 , dp[cur].push( encode(m) , V ) , code[y-1] = 0;                //停止                dp[cur].push( encode(m) , V );            }            else if ( left == 1 ) //同上            {                if ( x < n && g[x+1][y] == '_' ) code[y-1] = 2 , dp[cur].push( encode(m) , V ) , code[y-1] = 0;                if ( y < m && g[x][y+1] == '_' ) code[y] = 1 , dp[cur].push( encode(m) , V ) , code[y] = 0;            }            else            {                dp[cur].push( encode(m) , V );                if ( y < m && g[x][y+1] == '_' ) code[y] = 2 , dp[cur].push( encode(m) , V ) , code[y] = 0;            }        }        else //双联通        {            if ( left == up && left == 1 ) //必须相等 且都为1,那么此格子可以作为一个拐点            {                dp[cur].push( encode(m) , V );            }        }    }}LL solve(){    cur = 0;    dp[0].clear();    dp[0].push( 0 , 1 );    for( int i = 1; i <= n; i++ )    {        pre = cur , cur ^= 1;        dp[cur].clear();        for( int k = 0; k < ( dp[pre].size ); k++ )        dp[cur].push( dp[pre].state[k]<<inc , dp[pre].value[k] );        for( int j = 1; j <= m; j++ )        {            pre = cur , cur ^= 1 , dp[cur].clear();            for( int k = 0; k < dp[pre].size; k++ ) DP( i , j , k );        }    }    for( int k = 0; k < dp[cur].size; k++ )        if ( dp[cur].state[k] == 0 ) return dp[cur].value[k];    return 0;}void init(){    char s[100];    getchar();    for (int i=1;i<=n;i++){scanf("%s",s+1);for (int j=1;j<=m;j++)if (n<m) g[j][i] = s[j];    else g[i][j] = s[j];}if (n<m) swap(n,m);}int main(){        scanf("%d%d",&n,&m);        init();        printf("%lld\n",solve());    return 0;}



0 0
原创粉丝点击