【BZOJ 2669】 2669: [cqoi2012]局部极小值 (状压DP+容斥原理)

2669: [cqoi2012]局部极小值

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 667  Solved: 350

Description

有一个nm列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。
给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。

Input

输入第一行包含两个整数nm(1<=n<=4, 1<=m<=7),即行数和列数。以下n行每行m个字符,其中“X”表示局部极小值,“.”表示非局部极小值。

Output

输出仅一行,为可能的矩阵总数除以12345678的余数。

Sample Input

3 2
X.
..
.X

Sample Output

60

HINT

Source

 

【分析】

  我好蠢啊。。。

  保证每个数各不相同,又有大小关系,那么、、数字从小到大填。

  其实局部极小值<=8的,这个可以状压,$f[i][j]$表示填了前i个数,局部极小值被填的状态是j的方案数。

  有:

  $f[i][j]=f[i-1][j]*(p[j]-i+1)+f[i-1][j-(1<<X)]$

  但是,还要保证一点是非极小值一定非极小,上面没有保证,

  所以枚举哪些非极小弄成了极小,容斥算出正确答案即可。

  复杂度?$O(dfs*n*m*8*2^8)$大概是这样吧。。数据很小嘛。。。

 

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 #define Mod 12345678
  8 #define LL long long
  9 
 10 int a[5][8],num[5][8],p[310];
 11 int n,m;
 12 int bx[10]={0,1,0,-1,0,1,-1,1,-1},
 13     by[10]={0,0,1,0,-1,1,-1,-1,1};
 14 char s[10];
 15 bool vis[5][8];
 16 LL f[30][310],ans=0;
 17 
 18 LL get_ans()
 19 {
 20     int cnt=0;
 21     for(int i=1;i<=n;i++)
 22      for(int j=1;j<=m;j++) if(a[i][j]==1) num[i][j]=++cnt;
 23     for(int k=0;k<=(1<<cnt)-1;k++)
 24     {
 25         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) vis[i][j]=1;
 26         for(int i=1;i<=n;i++)
 27          for(int j=1;j<=m;j++) if(a[i][j]==1&&((1<<num[i][j]-1)&k)==0)
 28          {
 29              for(int l=1;l<=8;l++)
 30              {
 31                 int nx=i+bx[l],ny=j+by[l];
 32                 vis[nx][ny]=0;
 33              }
 34          }
 35         p[k]=0;
 36         for(int i=1;i<=n;i++)
 37          for(int j=1;j<=m;j++) if(vis[i][j]&&a[i][j]==0) {p[k]++;vis[i][j]=0;}
 38         for(int i=1;i<=cnt;i++) if((1<<i-1)&k) p[k]++;
 39     }
 40     memset(f,0,sizeof(f));
 41     f[0][0]=1;
 42     for(int i=1;i<=n*m;i++)
 43      for(int j=0;j<=(1<<cnt)-1;j++)
 44      {
 45          f[i][j]=f[i-1][j]*(p[j]-i+1);f[i][j]%=Mod;
 46          for(int k=1;k<=cnt;k++) if((1<<k-1)&j)
 47          {
 48              f[i][j]+=f[i-1][j-(1<<k-1)];
 49              f[i][j]%=Mod;
 50          }
 51      }
 52     return f[n*m][(1<<cnt)-1];
 53 }
 54 
 55 void dfs(int x,int y,int f)
 56 {
 57     if(y==m+1) {dfs(x+1,1,f);return;}
 58     if(x==n+1)
 59     {
 60         ans+=f*get_ans();
 61         ans=(ans%Mod+Mod)%Mod;
 62         return;
 63     }
 64     if(a[x][y]==1) {dfs(x,y+1,f);return;}
 65     bool ok=1;
 66     for(int i=1;i<=8;i++)
 67     {
 68         int nx=x+bx[i],ny=y+by[i];
 69         if(nx<1||nx>n||ny<1||ny>m) continue;
 70         if(a[nx][ny]==1) {ok=0;break;}
 71     }
 72     if(ok)
 73     {
 74         a[x][y]=1;
 75         dfs(x,y+1,-f);
 76         a[x][y]=0;
 77     }
 78     dfs(x,y+1,f);
 79 }
 80 
 81 int main()
 82 {
 83     scanf("%d%d",&n,&m);
 84     for(int i=1;i<=n;i++)
 85     {
 86         scanf("%s",s+1);
 87         for(int j=1;j<=m;j++)
 88         {
 89             if(s[j]=='X') a[i][j]=1;
 90             else a[i][j]=0;
 91         }
 92     }
 93     for(int i=1;i<=n;i++)
 94      for(int j=1;j<=m;j++) if(a[i][j]==1)
 95      {
 96          for(int k=1;k<=8;k++)
 97          {
 98              int nx=i+bx[i],ny=j+by[i];
 99              if(nx<1||nx>n||ny<1||ny>m) continue;
100              if(a[nx][ny]==1) {printf("0\n");return 0;}
101          }
102      }
103     // memset(vis,1,sizeof(vis));
104     dfs(1,1,1);
105     printf("%lld\n",ans);
106     return 0;
107 }
View Code

 

2017-04-06 10:08:51

posted @ 2017-04-06 10:08  konjak魔芋  阅读(288)  评论(0编辑  收藏  举报