[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;
}
posted @ 2021-10-24 00:11  I_N_V  阅读(145)  评论(0编辑  收藏  举报