bzoj2669[cqoi2012]局部极小值 容斥+状压dp
2669: [cqoi2012]局部极小值
Time Limit: 3 Sec Memory Limit: 128 MB
Submit: 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; }
If you live in the echo,
your heart never beats as loud.
如果你生活在回声里,
你的心跳声永远不会轰鸣作响。