[BZOJ2669][cqoi2012]局部极小值 状压dp+容斥原理

2669: [cqoi2012]局部极小值

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 1006  Solved: 539
[Submit][Status][Discuss]

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个

所以考虑状压dp。

设f[i][S]表示从小向大放入了i个数字,局部极小值为S的情况下的方案数。

显然我们要讨论当前第i个数放入的位置。

如果放在了X位置,那么f[i][S]+=f[i-1][S-(1<<(j-1))]

如果没放在X位置,那么根据定义,X位置和X位置周围的位置是不能放的,同事之前已经放了数的i-1个位置是不能放的。

所以预处理每个状态可以放的位置数sta[S],f[i][j]=(f[i][S]+f[i-1][S]*max(sta[S]-i+1,0))

 

然而这样算出的答案并不是正确答案,因为我们只保证了X位置为局部极小值,没保证非X位置不是局部极小值。

所以利用容斥原理枚举那些非X位置是局部极小值后加加减减即可。

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<algorithm>
 7 #define mod 12345678
 8 using namespace std;
 9 inline int read() {
10     int x=0,f=1;char ch=getchar();
11     for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
12     for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
13     return x*f;
14 }
15 int n,m,ans;
16 int tx[8]={-1,-1,-1,0,0,1,1,1},ty[8]={-1,0,1,-1,1,-1,0,1};
17 char s[30][30];
18 int ax[30],ay[30],vis[30][30],sta[1000],f[30][1000];
19 int solve() {
20     int cnt=0;
21     for(int i=1;i<=n;i++)
22         for(int j=1;j<=m;j++) if(s[i][j]=='X') ax[++cnt]=i,ay[cnt]=j;
23     int tot=(1<<cnt)-1;
24     for(int i=0;i<=tot;i++) {
25         memset(vis,0,sizeof(vis));
26         for(int j=1;j<=cnt;j++) {
27             if(i&(1<<(j-1))) continue;
28             int x=ax[j],y=ay[j];
29             vis[x][y]=1;
30             for(int k=0;k<8;k++) {
31                 int tox=x+tx[k],toy=y+ty[k];
32                 vis[tox][toy]=1;
33             }
34         }
35         int tmp=0;
36         for(int j=1;j<=n;j++) for(int k=1;k<=m;k++) if(vis[j][k]) tmp++;
37         sta[i]=n*m-tmp;
38     }
39     memset(f,0,sizeof(f));
40     f[0][0]=1;
41     for(int i=1;i<=n*m;i++) {
42         for(int j=0;j<=tot;j++) {
43             f[i][j]=(f[i][j]+f[i-1][j]*max(sta[j]-i+1,0))%mod;
44             for(int k=1;k<=cnt;k++) {
45                 if(j&(1<<(k-1))) {f[i][j]+=f[i-1][j-(1<<(k-1))];f[i][j]%=mod;}
46             }
47         }
48     }
49     return f[n*m][tot];
50 }
51 inline void dfs(int x,int y,int k) {
52     if(y==m+1) {dfs(x+1,1,k);return;}
53     if(x==n+1) {
54         ans=ans+solve()*((k%2==0)?1:-1);ans%=mod;
55         ans+=mod;ans%=mod;
56         return;
57     }
58     dfs(x,y+1,k);
59     bool flag=0;
60     for(int i=0;i<8;i++) {
61         int tox=x+tx[i],toy=y+ty[i];
62         if(s[tox][toy]=='X') {flag=1;break;}
63     }
64     if(!flag&&s[x][y]!='X') {
65         s[x][y]='X';
66         dfs(x,y+1,k+1);
67         s[x][y]='.';
68     }
69     return;
70 }
71 int main() {
72     n=read(),m=read();
73     for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
74     dfs(1,1,0);
75     printf("%d\n",((ans%mod)+mod)%mod);
76 }
View Code

 

posted @ 2018-10-09 14:11  wls001  阅读(237)  评论(1编辑  收藏  举报