习题:Xenia and Dominoes(状压DP&容斥)

题目

传送门

思路

首先我们考虑如果只有\(x\)\(.\)该如何去做

\(dp[i][j]\)表示前i列,第i列的状态为j,前i-1列均被填满的方案总数

先考虑横着放

对于\(dp[i][j]\)这个状态,那么必须先将其中的0填满,再先外转移,同时要保证骨牌是横着放对\(dp[i+1][j']\)是合法的

再考虑竖着放

其实就是看是否有连续的0出现

通过这个DP就可以求出方案总数

这个题目中保证空位只有1个

我们考虑,如果要保证空位的合法性,那么至少有一个骨牌的短的那一面是朝向空位的

考虑一个容斥,至少有i个骨牌是朝着空位,我们考虑选i个方向,在这些位置上强制不准放,再跑DP

注意到空位上也要保证不可以放

代码

#include<iostream>
#include<cstring>
using namespace std;
const int mod=1e9+7;
int m;
int x,y;
char a[4][10005];
int b[4][10005],vis[(1<<4)],nxt[10005];
int dx[5]={0,0,1,-1};
int dy[5]={1,-1,0,0};
long long dp[10005][(1<<3)],ans;
bool inside(int x,int y)
{
    if(0<=x&&x<=2&&1<=y&&y<=m)
        return 1;
    return 0;
}
int init(int s)
{
    memset(nxt,0,sizeof(nxt));
    for(int i=0;i<=2;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(a[i][j]=='.')
                b[i][j]=0;
            if(a[i][j]=='O'||a[i][j]=='X')
                b[i][j]=1;
        }
    }
    for(int i=0;i<=3;i++)
    {
        if(s&(1<<i))
        {
            int tx=x+dx[i]*2;
            int ty=y+dy[i]*2;
            if(inside(tx,ty)==0)
                s=s-(1<<i);
            else if(b[x+dx[i]][y+dy[i]]==1||b[x+dx[i]*2][y+dy[i]*2]==1)
                s=s-(1<<i);
            else
            {
                for(int j=1;j<=2;j++)
                    b[x+dx[i]*j][y+dy[i]*j]=1;
            }
        }
    }
    for(int i=1;i<=m;i++)
        for(int j=0;j<3;j++)
            if(b[j][i])
                nxt[i]=nxt[i]|(1<<j);
    return s;
}
int count_two(int val)
{
    int ret=0;
    while(val)
    {
        ret=ret+(val&1);
        val>>=1;
    }
    return ret;
}
long long solve()
{
    memset(dp,0,sizeof(dp));
    dp[1][nxt[1]]=1;
    if((nxt[1]&(1<<0))==0&&(nxt[1]&(1<<1))==0)
        dp[1][nxt[1]|(1<<0)|(1<<1)]=1;
    if((nxt[1]&(1<<1))==0&&(nxt[1]&(1<<2))==0)
        dp[1][nxt[1]|(1<<1)|(1<<2)]=1;
    for(int i=2;i<=m;i++)
    {  
        for(int j=nxt[i-1];j<(1<<3);j=(j+1)|nxt[i-1])
        {
            if((j^((1<<3)-1))&nxt[i])
                continue;
            dp[i][(j^((1<<3)-1))|nxt[i]]=(dp[i][(j^((1<<3)-1))|nxt[i]]+dp[i-1][j])%mod;
        }
        if((nxt[i]&(1<<0))==0&&(nxt[i]&(1<<1))==0)
        {
            dp[i][3]=(dp[i][3]+dp[i][0])%mod;
    		dp[i][7]=(dp[i][7]+dp[i][4])%mod;
		}
		if((nxt[i]&(1<<1))==0&&(nxt[i]&(1<<2))==0)
		{
			dp[i][6]=(dp[i][6]+dp[i][0])%mod;
            dp[i][7]=(dp[i][7]+dp[i][1])%mod;
    	}
	}
    return dp[m][(1<<3)-1];
}
int main()
{
    cin>>m;
    for(int i=0;i<=2;i++)
        for(int j=1;j<=m;j++)
        {
            cin>>a[i][j];
            if(a[i][j]=='O')
            {
                x=i;
                y=j;
            }
        }
    for(int i=1;i<(1<<4);i++)
    {
        int now=init(i);
        if(vis[now]||now==0)
            continue;
        ans=(ans+solve()*(count_two(now)%2?1:-1))%mod;
        vis[now]=1;
    }
    cout<<(ans+mod)%mod;
    return 0;
}
posted @ 2020-08-09 15:07  loney_s  阅读(157)  评论(0)    收藏  举报