bzoj 2669 [cqoi2012]局部极小值 DP+容斥
2669: [cqoi2012]局部极小值
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 838 Solved: 444
[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个。
我们可以用f[i][sta]表示填了1-i个数,已经填完了状态为sta的方案数,然后剩下的
随便去填,但是这样会有一个问题,就是在其它'.'的位置,如果填成了局部最小解怎么办。
这样就会有多的方案算进去。
所以就要用容斥的方法去解决这个问题。
那f[i][sta]怎么算。
格式写不出来。
cnt的话就是暴力2*8*n*m*9 一次60000的复杂度而已,9代表判断周围。
f[i][j]=f[i-1][j](前面填了1-i中就已经填好了j这个状态,那么剩下就在cntj-(i-1))选一个,(因为cnt包含了
局部最小解个数),加上,所有的当前,填这一个最小解的方案数。因为填的数是不同的,所以是不同
状态。
复杂度是 2*8 *2*8*n*m*9差不多10000000这是极限
1 #pragma GCC optimize(2) 2 #pragma G++ optimize(2) 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 #include<cstdio> 7 #include<cstring> 8 9 #define MOD 12345678 10 using namespace std; 11 const int dx[]={-1,-1,-1,0,0,1,1,1,0}; 12 const int dy[]={-1,0,1,-1,1,-1,0,1,0}; 13 inline int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} 17 while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 21 int n,m,ans; 22 char s[10][10]; 23 24 int Calculate() 25 { 26 static pair<int,int> stack[10]; 27 static int cnt[1<<8],f[30][1<<8]; 28 int i,j,k,sta,top=0; 29 memset(cnt,0,sizeof cnt); 30 memset(f,0,sizeof f); 31 for(i=1;i<=n;i++) 32 for(j=1;j<=m;j++) 33 if(s[i][j]=='X') 34 stack[++top]=pair<int,int>(i,j); 35 for(sta=0;sta<1<<top;sta++) 36 { 37 static bool unfilled[10][10]; 38 memset(unfilled,0,sizeof unfilled); 39 for(i=1;i<=top;i++) 40 if(~sta&(1<<i-1)) 41 unfilled[stack[i].first][stack[i].second]=true; 42 for(i=1;i<=n;i++) 43 for(j=1;j<=m;j++) 44 { 45 for(k=0;k<9;k++) 46 if(unfilled[i+dx[k]][j+dy[k]]) 47 break; 48 if(k==9) 49 cnt[sta]++; 50 } 51 } 52 f[0][0]=1; 53 for(i=1;i<=n*m;i++) 54 for(sta=0;sta<1<<top;sta++) 55 { 56 (f[i][sta]+=(long long)f[i-1][sta]*max(cnt[sta]-i+1,0))%=MOD; 57 for(j=1;j<=top;j++) 58 if(sta&(1<<j-1)) 59 (f[i][sta]+=f[i-1][sta^(1<<j-1)])%=MOD; 60 } 61 return f[n*m][(1<<top)-1]; 62 } 63 void DFS(int x,int y,int cnt) 64 { 65 int i; 66 if(y==m+1) 67 { 68 DFS(x+1,1,cnt); 69 return ; 70 } 71 if(x==n+1) 72 { 73 (ans+=Calculate()*cnt)%=MOD; 74 return ; 75 } 76 DFS(x,y+1,cnt); 77 for(i=0;i<9;i++) 78 if(s[x+dx[i]][y+dy[i]]=='X') 79 break; 80 if(i==9) 81 { 82 s[x][y]='X'; 83 DFS(x,y+1,-cnt); 84 s[x][y]='.'; 85 } 86 } 87 int main() 88 { 89 int i,j,k; 90 n=read(),m=read(); 91 for(i=1;i<=n;i++) 92 scanf("%s",s[i]+1); 93 for(i=1;i<=n;i++) 94 for(j=1;j<=m;j++) 95 if(s[i][j]=='X') 96 for(k=0;k<8;k++) 97 if(s[i+dx[k]][j+dy[k]]=='X') 98 return puts("0"),0; 99 DFS(1,1,1); 100 cout<<(ans+MOD)%MOD<<endl; 101 }
f