luogu P3160 [CQOI2012]局部极小值
题面传送门
首先这个\(nm\)非常小,于是我们可以考虑状压。
因为这个最低点肯定不能八连通,所以最多只有\(8\)个这种最低点。
如果不考虑.
的格子不能为X
的情况,容易状压:设\(dp_{i,S}\)表示已经放了前\(i\)个数,钦定为最低点的集合为\(S\),数一下还有哪些地方能放即可。时间复杂度大概是\(O(nm2^8)\)。
考虑恰好转至少,枚举至少为X
的地方容斥,然后dp即可,时间复杂度大概是\(O(3^8(nm)^2+\sum 2^8nm^3)\),反正最后一个hack是开了O2贴着时限跑过去的。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (7+2)
#define M (1<<8)
#define K (6)
#define mod 12345678
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,m,cnt,Id[N][N],Fl[N][N];ll dp[N*N][M];char A[N][N];ll Ans;
int xp[8]={1,1,1,-1,-1,-1,0,0};int yp[8]={-1,0,1,-1,0,1,-1,1};
I int calc(int S){
Me(Fl,0);int i,j,h;for(i=1;i<=n;i++){
for(j=1;j<=m;j++) {if(A[i][j]=='X'&&S>>(Id[i][j]-1)&1)for(Fl[i][j]=1,h=0;h<8;h++) Fl[i+xp[h]][j+yp[h]]=1;}
}
int cnt=0;for(i=1;i<=n;i++) for(j=1;j<=m;j++) cnt+=!Fl[i][j];return cnt;
}
I void Solve(int w){//printf("%d\n",w);
int i,j,h;dp[0][0]=1;for(i=0;i<n*m;i++){
for(j=0;j<(1<<w);j++) dp[i+1][j]=0;
for(j=0;j<(1<<w);j++) {if(!dp[i][j]) continue;
int x=calc(((1<<w)-1)^j);if(x>i)dp[i+1][j]=(dp[i][j]*(x-i)+dp[i+1][j])%mod;
for(h=1;h<=w;h++) !(j>>(h-1)&1)&&(dp[i+1][j^(1<<h-1)]=(dp[i][j]+dp[i+1][j^(1<<h-1)])%mod);
}
}Ans+=((w&1)^(cnt&1)?-dp[n*m][(1<<w)-1]:dp[n*m][(1<<w)-1]);//cerr<<Ans<<'\n';
}
I void dfs(int x,int y,int w){
if(y==m+1) return dfs(x+1,1,w);if(x==n+1) return Solve(w);if(A[x][y]=='X') return Id[x][y]=w+1,dfs(x,y+1,w+1),Id[x][y]=0,void();
int Fl=0;for(int i=0;i<8;i++) if(A[x+xp[i]][y+yp[i]]=='X'){Fl=1;break;}!Fl&&(A[x][y]='X',Id[x][y]=w+1,dfs(x,y+1,w+1),Id[x][y]=0,A[x][y]='.');dfs(x,y+1,w);
}
int main(){
freopen("1.in","r",stdin);
int i,j,h;scanf("%d%d",&n,&m);for(i=1;i<=n;i++)for(scanf("%s",A[i]+1),j=1;j<=m;j++)cnt+=(A[i][j]=='X');
for(i=1;i<=n;i++) for(j=1;j<=m;j++) for(h=0;h<8;h++) if(A[i][j]=='X'&&A[i+xp[h]][j+yp[h]]=='X') {puts("0");return 0;}dfs(1,1,0);printf("%lld\n",(Ans%mod+mod)%mod);
}