1. 程式人生 > >2669[cqoi2012]局部極小值 容斥+狀壓dp

2669[cqoi2012]局部極小值 容斥+狀壓dp

方案 容斥 ng- scrip pad iostream set mes scan

2669: [cqoi2012]局部極小值

Time Limit: 3 Sec Memory Limit: 128 MB
Submit: 774 Solved: 411
[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

容斥,推一推可以得到X的個數不超過8個(雖然我不知道是怎麽推的)
枚舉,從小到大填數,狀壓dp可以計算出對於此種圖的填數方案
用cnt[s]表示狀態s下可以填數的方案(包括之前已經填過的X但不包括沒填的X)
f[i][s]轉移就得到啦(水一波)

這樣我們可以保證X的位置一定是周圍最小的,但卻不能保證其他位置不會出現多余的‘X‘
於是我們dfs出每一個可以為X的地方,容斥一下就好啦

推薦blog
http://blog.csdn.net/popoqqq/article/details/48028773



取模有毒
a+=b;if(a>=mod)a-=mod;
如果b是負數的話..就炸了!!
調了1h..

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define mod 12345678
#define ll long long
using namespace std;
int n,m,tp,cnt[1<<9],ok[10][10];
int dx[]={0,0,1,-1,1,-1,1,-1,0};
int dy[]={1
,-1,0,0,1,-1,-1,1,0}; char mp[10][10];ll ans,f[30][1<<9]; struct node{int x,y;}p[10]; int dp(){ memset(cnt,0,sizeof(cnt)); memset(f,0,sizeof(f));tp=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(mp[i][j]==X) p[++tp]=(node){i,j}; for(int st=0;st<(1<<tp);st++){ memset(ok,0,sizeof(ok)); for(int j=1;j<=tp;j++) if(!(st&(1<<(j-1))))ok[p[j].x][p[j].y]=1; for(int i=1;i<=n;i++) for(int k,j=1;j<=m;j++){ for(k=0;k<9;k++) if(ok[i+dx[k]][j+dy[k]])break; if(k==9)cnt[st]++; } } f[0][0]=1; for(int i=1;i<=n*m;i++) for(int st=0;st<(1<<tp);st++){ (f[i][st]+=f[i-1][st]*max(0,cnt[st]-i+1))%=mod; for(int k=1;k<=tp;k++) if((1<<(k-1))&st)(f[i][st]+=f[i-1][st^(1<<(k-1))])%=mod; } return f[n*m][(1<<tp)-1]; } void dfs(int x,int y,int c){ int t; if(x==n+1){ (ans+=dp()*(c&1?-1:1))%=mod; return; } if(y==m)dfs(x+1,1,c); else dfs(x,y+1,c); for(t=0;t<9;t++)if(mp[dx[t]+x][dy[t]+y]==X)break; if(t<9)return; mp[x][y]=X; if(y==m)dfs(x+1,1,c+1); else dfs(x,y+1,c+1); mp[x][y]=.; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%s",mp[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(mp[i][j]==X) for(int k=0;k<8;k++){ int nx=i+dx[k],ny=j+dy[k]; if(mp[nx][ny]==X){puts("0");return 0;} } dfs(1,1,0); ans<0?ans+=mod:1; cout<<ans; return 0; }

2669[cqoi2012]局部極小值 容斥+狀壓dp