bzoj 2669 状压DP

  因为最多有8个'X',所以我们可以用w[i][s]来表示现在我们填了前i个数,填的X的为S,因为每次新加进来的数都不影响前面的最小值,所以我们可以随便添加,这样就有了剩下所有位置的方案,每次都这样转移。

  但是这样会造成不是规定的地方出现局部最小值的情况,对于这样的情况,我们只需要枚举所有可能成为局部最小值的不合法状态来做容斥就可以了。

  反思:这道题的容斥开始写错了,本来应该是判奇偶来判断正负,写成了全是负的,还是A了,应该是后面的容斥没有合法方案所以符号无所谓的关系,真是rp++。

/**************************************************************

    Problem: 2669

    User: BLADEVIL

    Language: C++

    Result: Accepted

    Time:0 ms

    Memory:928 kb

****************************************************************/

 

//By BLADEVIL

#include <cstdio>

#include <cstring>

#define d39 12345678

#define get(x) ((x&1)?-1:1)

 

using namespace std;

 

char s[8][10];

int map[8][10],flag[8][10],w[30][1000],X[10],Y[10],tot[1000],tmp[8][10];

int ans,sum,n,m;

const int go[8][2]={{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1}};

 

int solve() {

    int cnt=0,x,y,flag;

    for (int i=1;i<=n;i++) 

        for (int j=1;j<=m;j++) if (map[i][j]) X[cnt]=i,Y[cnt++]=j;

    for (int p=0;p<(1<<cnt);p++) {

        tot[p]=0;

        memset(tmp,0,sizeof tmp);

        for (int i=0;i<cnt;i++) if (p&(1<<i)) tmp[X[i]][Y[i]]=1;

        for (int i=1;i<=n;i++) 

            for (int j=1;j<=m;j++) if (!tmp[i][j]) {

                flag=1;

                for (int k=0;k<8&&flag;k++) {

                    x=i+go[k][0],y=j+go[k][1];

                    if (x<1||x>n||y<1||y>m) continue;

                    if (tmp[x][y]) flag=0;

                }

                if (flag) tot[p]++;

        }

    }

    memset(w,0,sizeof w);

    w[0][0]=1;

    int opp;

    for (int i=1;i<=sum;i++) {

        for (int p=0;p<(1<<cnt);p++) if (w[i-1][p]) {  

            opp=0;

            for (int j=0;j<cnt;j++) if (!(p&(1<<j))) 

                opp|=(1<<j),(w[i][p|(1<<j)]+=w[i-1][p])%=d39;

            (w[i][p]+=w[i-1][p]*(tot[opp]-(i-1))%d39)%=d39;

        }

   }

   return w[sum][(1<<cnt)-1];

}

 

void ie(int x,int y,int t){

    map[x][y]=1;

    int i,j,ret;

    for (int k=0;k<8;k++) {

        i=x+go[k][0],j=y+go[k][1];

        if (i<1||i>n||j<1||j>m) continue;

        if (flag[i][j]) continue;

        flag[i][j]=t;

    }

    ret=solve();

    ans=(ans+get(t)*ret)%d39;

    for (j=y+1;j<=m;j++) if (flag[x][j]==0) ie(x,j,t+1);

    for (i=x+1;i<=n;i++) 

        for (j=1;j<=m;j++) if (flag[i][j]==0) ie(i,j,t+1);

    map[x][y]=0;

    for (int k=0;k<8;k++) {

        i=x+go[k][0],j=y+go[k][1];

        if (i<1||i>n||j<1||j>m) continue;

        if (flag[i][j]==t) flag[i][j]=0;

    }

}

 

int main(){

    scanf("%d%d",&n,&m);

    int cur=1,x,y;

    sum=n*m;

    for (int i = 1;i <= n;i ++) {

        scanf("%s",s[i]);

        for (int j=0;j<m;j++)map[i][j+1]=s[i][j]=='X'?1:0;

    }

    for (int i=1;i<=n&&cur;i++) {

        for (int j=1;j<=m&&cur;j++) if (map[i][j]) {

            if (flag[i][j]==-1) cur=0;

            flag[i][j]=-1;

            for (int k=0;k<8;k++) {

               x=i+go[k][0],y=j+go[k][1];

               if (x<1||x>n||y<1||y>m) continue;

               flag[x][y]=-1;

           }

       }

    }

    if (!flag) {

        printf("0\n");

        return 0;

    }

    ans=solve();

    for (int i=1;i<=n;i++) 

        for (int j=1;j<=m;j++) if (flag[i][j]==0) ie(i,j,1);

    (ans+=d39)%=d39;

    printf("%d\n",ans);

    return 0;

}

 

你可能感兴趣的:(ZOJ)