[BZOJ2669][cqoi2012]局部极小值 状压dp+容斥原理
2669: [cqoi2012]局部极小值
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 1006 Solved: 539
[Submit][Status][Discuss]
Description
有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。
给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。
Input
输入第一行包含两个整数n和m(1<=n<=4, 1<=m<=7),即行数和列数。以下n行每行m个字符,其中“X”表示局部极小值,“.”表示非局部极小值。
Output
输出仅一行,为可能的矩阵总数除以12345678的余数。
Sample Input
3 2
X.
..
.X
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 }
O(∩_∩)O~ (*^__^*) 嘻嘻…… O(∩_∩)O哈哈~