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;
}

 

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