[CQOI2012]局部极小值
题目描述
题解
首先可以知道一个矩阵最多只能有8个特殊点,因此这8个点的状态很好表示,并且n,m数据范围很小,可以考虑用状压dp来计算
因为局部极小值为周围最小的,可以考虑从小到大填数
设dp[i][j]为填了前i个数,特殊点状态为j的方案数(j的二进制第i位表示第i个特殊点是否填上)
则转移方程
当第i数没填在特殊点位置
\(dp[i][j]+=dp[i-1][j]*max\{0,[re[j]-(i-1-|j|)]\} (|j|为已填特殊点的个数为)\)
\(re[i]\)表示状态为\(j\)时,新加入的数可以的填的位置个数,也就是新数只能填在已经填了的特殊点的周围,并且不能相邻未填的特殊点,这样就没法保证未填特殊点的值小于周围
另外可以将rej[j]算上状态j的已填特殊点个数,这样后面代码好写
则转移为\(dp[i][j]+=dp[i-1][j]*max\{0,[re[j]-(i-1)]\}\)
当第i数填在特殊点位置上
\(dp[i][j]+=\sum_{s\in j}dp[i-1][j-s]\)
但虽然这转移方程可以求出所有满足特殊点的答案,但没法保证除了特殊点,方案中其他位置也存在局部极小值
这就要用容斥原理
设集合\(a_{ij}\)为当(i,j)位置为特殊点
则\(N(\prod_{(i,j)为特殊点} \:\:\:\:a_{ij}\prod_{(i,j)不为特殊点} \:\:\:\:(1-a_{ij}))\)
\(\:\:=\sum_{i=0}^{最多从非题目所给特殊点中能最多选多少个特殊}(-1)^i \cdot \sum_{(i,j)为特殊点(包括题目给的)} \:\:\: \:\:\:\:\:\:\:N(a_{ij})\)
用容斥搜索dfs即可
时间复杂度:
因为一个矩阵最多有8个特殊点,n*m最大为28,所以容斥的状态最多为\(\sum_{i=0}^8{28 \choose i}\)大约在\(10^6-10^7\)
但是其中有大部分状态都是没有用的,有用的大概也就\(10^4\)左右
所以最终极限复杂度大概为\(O(nm2^8\cdot 10^4)\)
代码实现
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<queue>
#include<stdlib.h>
#define ll long long
using namespace std;
const int maxn=1000000+10101;
const int MOD=12345678;
const int inf=2147483647;
int read(){
int x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int n,m,ma[5][8],book[5][8],dp[29][257],re[257],cnt;
int x1[8]={-1,-1,0,1,1,1,0,-1};
int yy[8]={0,-1,-1,-1,0,1,1,1};
struct wzq{int i,j;}pos[9];
ll getdp(){
cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){if(ma[i][j])pos[++cnt]=(wzq){i,j};}
}
memset(dp,0,sizeof(dp));
for(int j=0;j<(1<<cnt);j++){
int now=j,sum=0,tot=0;
memset(book,0,sizeof(book));
while(now){
sum++;
if(now&1)tot++,book[pos[sum].i][pos[sum].j]=1;
now>>=1;
}
for(int i1=1;i1<=n;i1++){
for(int i2=1;i2<=m;i2++){
if(book[i1][i2] || ma[i1][i2])continue;bool fa=false;
for(int i=0;i<8;i++){
int x=x1[i]+i1,y=yy[i]+i2;
if(x<=0 || x>n || y<=0 || y>m)continue;
if(ma[x][y]==1 && book[x][y]==0){fa=true;break;}
}
if(fa)continue;tot++;
}
}
re[j]=tot;
}
dp[0][0]=1;
for(int i=1;i<=n*m;i++){
for(int j=0;j<(1<<cnt);j++){
dp[i][j]+=(dp[i-1][j]*(max(0,(re[j]-i+1))%MOD))%MOD;dp[i][j]%=MOD;
for(int k=0;k<cnt;k++){
if(!(j&(1<<k)))continue;
dp[i][j]=(dp[i-1][j^(1<<k)]+dp[i][j])%MOD;
}
}
}
return dp[n*m][(1<<cnt)-1]%MOD;
}
ll ans;
void dfs(int x,int y,int z){
if(x==n+1){ans=(ans+z*getdp()%MOD)%MOD;return;}
if(y==m+1){dfs(x+1,1,z);return ;}
dfs(x,y+1,z);if(ma[x][y])return ;
for(int i=0;i<8;i++){
int xxx=x1[i]+x,yyy=yy[i]+y;
if(xxx<=0 || xxx>n || yyy<=0 || yyy>m)continue;
if(ma[xxx][yyy])return ;
}
ma[x][y]=1;dfs(x,y+1,-z);ma[x][y]=0;return;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++){
char ch[9];scanf("%s",ch);
for(int j=0;j<m;j++){if(ch[j]=='X')ma[i][j+1]=1;}
}
dfs(1,1,1);printf("%lld",(ans%MOD+MOD)%MOD);
return 0;
}