BZOJ2808 : 那些年我们画格子
若$\min(n,m)=1$,那么设$f[i][j][k]$表示考虑前$i$个格子,改变了$j$次颜色,$i$的颜色为$k$的方案数,直接转移即可。
否则$\min(n,m)\geq 2$,那么有解当且仅当第一二行重复得到整个图案或者第一二列重复得到整个图案。
假设是第一二行重复:
那么可以设$g[i][j][x][y]$表示考虑前$i$列,改变了$j$次颜色,第一行第$j$列颜色为$x$,第二行第$j$列颜色为$y$的方案数。
预处理出每种颜色会增加几次改变,然后转移即可。
若是第一二列重复,只需要转置这个矩阵,即可转化为第一二行重复。
这其中多算的是第一二行重复同时第一二列重复的情况,此时直接枚举左上角$4$个格子的颜色然后检验即可。
时间复杂度$O(n(m+k))$。
#include<cstdio> const int N=105,P=1000000007; int n,m,K,S,o,i,j,k,x,y,t,A,B,f[2][N*N][5],g[2][N*N][4][4],w[4][4],ans;char a[N][N],b[N][N]; inline int getid(char x){ if(x=='G')return 0; if(x=='B')return 1; if(x=='R')return 2; return 3; } inline void up(int&a,int b){a=a+b<P?a+b:a+b-P;} void check(int S){ b[1][1]=S&3; b[1][2]=S>>2&3; b[2][1]=S>>4&3; b[2][2]=S>>6; for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(i>2||j>2)b[i][j]=b[i&1?1:2][j&1?1:2]; for(t=0,i=1;i<=n;i++)for(j=1;j<=m;j++)if(a[i][j]!=b[i][j])t++; if(t>K)return; for(i=1;i<=n;i++)for(j=1;j<=m;j++) for(x=-1;x<=1;x++)if(i+x>=1&&i+x<=n)for(y=-1;y<=1;y++)if((x||y)&&j+y>=1&&j+y<=m) if(b[i][j]==b[i+x][j+y])return; ans++; } inline void getw(int x,int m){ for(int A=0;A<4;A++)for(int B=0;B<4;B++){ w[A][B]=0; for(int i=1;i<=m;i++){ int j=i&1?A:B; if(j!=b[x][i])w[A][B]++; } } } void solve(int n,int m){ getw(1,m); for(j=0;j<=K;j++)for(x=0;x<4;x++)for(y=0;y<4;y++)g[0][j][x][y]=0; for(o=x=0;x<4;x++)for(y=0;y<4;y++)if(w[x][y]<=K&&x!=y)g[0][w[x][y]][x][y]=1; for(i=2;i<=n;i++){ getw(i,m); for(j=0;j<=K;j++)for(x=0;x<4;x++)for(y=0;y<4;y++)g[o^1][j][x][y]=0; for(j=0;j<=K;j++)for(x=0;x<4;x++)for(y=0;y<4;y++)if(g[o][j][x][y]) for(k=0;k<4;k++)if(k!=x&&k!=y){ t=6-x-y-k; if(j+w[k][t]<=K){ up(g[o^1][j+w[k][t]][k][t],g[o][j][x][y]); } } o^=1; } for(j=0;j<=K;j++)for(x=0;x<4;x++)for(y=0;y<4;y++)up(ans,g[o][j][x][y]); } int main(){ scanf("%d%d%d",&n,&m,&K); for(i=1;i<=n;i++){ scanf("%s",a[i]+1); for(j=1;j<=m;j++)a[i][j]=getid(a[i][j]); } if(n==1||m==1){ for(f[0][0][4]=i=1;i<=n;i++)for(j=1;j<=m;j++){ for(k=0;k<=K;k++)for(x=0;x<5;x++)f[o^1][k][x]=0; for(k=0;k<=K;k++)for(x=0;x<5;x++)if(f[o][k][x])for(y=0;y<4;y++)if(y!=x)up(f[o^1][k+(y!=a[i][j])][y],f[o][k][x]); o^=1; } for(i=0;i<=K;i++)for(j=0;j<4;j++)up(ans,f[o][i][j]); }else{ for(S=0;S<1<<8;S++)check(S); ans=(P-ans)%P; for(i=1;i<=n;i++)for(j=1;j<=m;j++)b[i][j]=a[i][j]; solve(n,m); for(i=1;i<=n;i++)for(j=1;j<=m;j++)b[j][i]=a[i][j]; solve(m,n); } return printf("%d",ans),0; }