2669[cqoi2012]局部極小值 容斥+狀壓dp
阿新 • • 發佈:2017-12-13
方案 容斥 ng- scrip pad iostream set mes scan
Submit: 774 Solved: 411
[Submit][Status][Discuss]
取模有毒
a+=b;if(a>=mod)a-=mod;
如果b是負數的話..就炸了!!
調了1h..
2669: [cqoi2012]局部極小值
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 774 Solved: 411
[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
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