The 2017 ACM-ICPC Asia Jakarta Regional Contest L - Sacred Scarecrows/UVALive - 8144 (状压dp+容斥)

传送门

题目:

题意:多组输入,给你n*m(n<=14,m<=1e3)的字符矩阵,只包含 v 和 . 其中v是障碍物。

你需要在.上涂色,使得每一行都有格子被涂色,相邻两列必须有一列有格子被涂色。求最终的合法方案总数%1e9+7。

 思路:

一看到这个题意和n的范围,肯定是状压dp。

首先,如果直接暴力状压的话,我们需要枚举每一列,这一列的状态,上一列的状态,复杂度将会爆炸。

发现对于障碍物,我们直接开一个数组记录这一列合法的位置即可,至于相邻两列必有一列涂色,我们就设dp[i]为第i列必须涂色的合法方案数。则:

dp[0]=1

dp[i]=dp[i-1]*(2^(当前状态&这一列的合法位置状态 的1的个数)-1)  (i=1,2...,m)

dp[i]+=dp[i-2]*(2^(当前状态&这一列的合法位置状态 的1的个数)-1)  (i=2...,m)

最后答案就是dp[m]+dp[m-1],当然m=1的时候需要特判。

考虑到枚举所有子状态,我们可以直接使用容斥,将枚举子状态的复杂度去掉。

本题时限卡的非常严格,所有变量必须使用int,注意数组大小和取模,还有预处理2的幂次以及每个状态中1的个数。

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define mst(head,x,n) memset(head+1,x,n*sizeof(head[0]))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int maxn=1e3+5;
const int maxm=(1<<14)+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
const int mo=1e9+7;
int n,m,k;
int ok[maxn];
int ans,tmp;
int flag;
int dp[maxn];
char s[15][maxn];
int cnt[maxm],po[maxn];
template <typename T>
inline void read(T &X){
    X=0;int w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    if(w) X=-X;
}
int cal(int s){
    dp[0]=1;
    rep(i,1,m){
        int tmp=(po[cnt[s&ok[i]]]-1+mo)%mo;
        dp[i]=1LL*dp[i-1]*tmp%mo;
        if(i>1) dp[i]=(dp[i]+1LL*dp[i-2]*tmp%mo)%mo;
    }
    if(m==1) return dp[m];
    else return (dp[m]+dp[m-1])%mo;
}
int main(){
/*
#ifdef ONLINE_JUDGE
#else
    freopen("D:/Temp/in.txt", "r", stdin);
#endif
*/
    po[0]=1;
    rep(i,1,20) po[i]=(po[i-1]<<1)%mo;
    cnt[0]=0;
    rep(i,1,maxm-1) cnt[i]=cnt[i>>1]+(i&1);
    int T,cas=1;
    //read(T);
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        //read(n);read(m);
        rep(i,0,n-1){
            scanf("%s",s[i]+1);
        }
        rep(j,1,m){
            ok[j]=0;
            rep(i,0,n-1)
            if(s[i][j]=='.') ok[j]|=(1<<i);
        }
        ans=0;
        //cout<<"*";
        int mm=(1<<n)-1;
        rep(i,0,mm){
            if((n-cnt[i])&1){
                ans=(ans-cal(i)+mo)%mo;
            }
            else ans=(ans+cal(i))%mo;
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章