【CQOI 2012】【JZOJ 4700】【BZOJ 2669】局部极小值

    xiaoxiao2026-05-28  2

    Description

    抽象题意:在一个矩阵中依次填入数,要求只有规定的数是小于它周边的数。

    Solution

    先来考虑一下当每一个.都有与它相邻的X, 因为棋盘很小,最多有8个X, 用状态压缩: fi,S 表示当前选到数字i,状态为S,

    fi,S=fia,S(RSi+1)+kSfi1,K RS 表示在i的状态下,总共有多少个位置可以填, 我们发现,这样会算重,所有要用容斥原理来做, 复杂度: O(2m12knm) ,k表示有多少个X,m1表示可以填的位置数;

    Code

    #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #define fo(i,a,b) for(int i=a;i<=b;i++) #define OK(q,w) ((q)>0&&(w)>0&&(q)<=n&&(w)<=m) using namespace std; typedef long long LL; const int N=10,mo=12345678; int m,n,ans,m1; int a[N][N]; LL R[1<<10]; LL f[N*N][1<<10]; int er[21]; bool z[N][N],z1[N][N]; int fx[8][2]={{-1,0},{-1,-1},{0,-1},{-1,1},{1,-1},{1,1},{1,0},{0,1}}; bool OKX(int i,int j,int e) { fo(k,0,e)if(OK(i+fx[k][0],j+fx[k][1]))if(a[i+fx[k][0]][j+fx[k][1]])return 1; return 0; } int doit() { fo(I,0,er[m1+1]-1) { R[I]=0; fo(i,1,n)fo(j,1,m) { bool z=1; fo(k,0,7)if(OK(i+fx[k][0],j+fx[k][1])) if(a[i+fx[k][0]][j+fx[k][1]]&&!(er[a[i+fx[k][0]][j+fx[k][1]]]&I))z=0; if(((er[a[i][j]]&I)||!a[i][j])&&z)R[I]++; } } f[0][0]=1; fo(i,1,n*m) fo(I,0,er[m1+1]-1) { LL t=0; fo(j,1,m1)if(er[j]&I)t=(t+f[i-1][I-er[j]])%mo; f[i][I]=(f[i-1][I]*(R[I]-i+1)%mo+t)%mo; } return f[n*m][er[m1+1]-1]; } void ss(int q,int w,int e) { if(w>m)q++,w=1; if(q>n){ans=(ans+doit()*((e%2)?-1:1))%mo;return;} ss(q,w+1,e); if(!a[q][w]&&!z[q][w]&&z1[q][w]) { a[q][w]=++m1; z1[q+1][w-1]=z1[q+1][w]=z1[q+1][w+1]=0; ss(q,w+2,e+1); a[q][w]=0;m1--; z1[q+1][w-1]=z1[q+1][w]=z1[q+1][w+1]=1; } } int main() { int q,w,_; er[1]=1;fo(i,2,20)er[i]=er[i-1]<<1; scanf("%d",&_); while(_--) { scanf("%d%d",&n,&m); m1=0; fo(i,1,n) fo(j,1,m) { char ch=' ';a[i][j]=0; while(ch!='.'&&ch!='X')ch=getchar(); if(ch=='X')a[i][j]=++m1; } fo(i,1,n)fo(j,1,m) { z1[i][j]=!(z[i][j]=OKX(i,j,7)); if(a[i][j]&&z[i][j])m1=0; } if(!m1){printf("0\n");continue;} ans=0; ss(1,1,0); printf("%lld\n",(ans+mo)%mo); } return 0; }
    转载请注明原文地址: https://ju.6miu.com/read-1310138.html
    最新回复(0)