我们发现最终的合法图在黑白块的分界线处形成了不增或不降的序列 然后推出递推式: dpj,k=∑kt=0dpj−1,t 然后对于每一种情况dp就行了 当然黑白分割线水平或竖直的时候有情况重复 整张图全黑或全白的时候也有重复,需要减去 总之,照 sr 大神的说法,这是一道丧心病狂的计数题 代码如下:
#include <bits/stdc++.h> using namespace std; #define P 1000000007 #define M 51 #define Max(a,b) if(a<b)a=b #define Min(a,b) if(a>b)a=b int n,m,ans; char str[M][M]; int a[M][M]; int dp[M][M]; int up[M],down[M]; int getval(char c){ if(c=='B')return 1; if(c=='W')return 2; return 0; } void solve(){ memset(dp,0,sizeof(dp)); for(int i=1;i<=m;i++) up[i]=0,down[i]=n+1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ if(a[i][j]&1)Min(down[j],i); if(a[i][j]&2)Max(up[j],i); } for(int i=up[1];i<down[1];i++)dp[1][i]=1; for(int j=2;j<=m;j++) for(int k=up[j];k<down[j];k++) for(int t=k;t>=0;t--) dp[j][k]=(dp[j][k]+dp[j-1][t])%P; for(int i=0;i<=n;i++) ans=(ans+dp[m][i])%P; } bool check(int l1,int r1,int l2,int r2,int v){ for(int i=l1;i<=r1;i++) for(int j=l2;j<=r2;j++) if(a[i][j]==v)return 0; return 1; } void delt(){ int f=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ f|=a[i][j]=getval(str[i][j]); } if((f&1)==0)ans-=3; if((f&2)==0)ans-=3; for(int i=1;i<m;i++){ if(check(1,n,1,i,1)&&check(1,n,i+1,m,2))ans--; if(check(1,n,1,i,2)&&check(1,n,i+1,m,1))ans--; } for(int i=1;i<n;i++){ if(check(1,i,1,m,1)&&check(i+1,n,1,m,2))ans--; if(check(1,i,1,m,2)&&check(i+1,n,1,m,1))ans--; } } int main(){ scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",str[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=getval(str[i][j]); solve(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][m-j+1]=getval(str[i][j]); solve(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[n-i+1][j]=getval(str[i][j]); solve(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[n-i+1][m-j+1]=getval(str[i][j]); solve(); delt(); printf("%d\n",ans); return 0; }