BZOJ4572 : [Scoi2016]围棋
考虑反面,用状压DP求出不合法的方案数。
设$f[i][j][S][x][y]$表示填到了$(i,j)$,轮廓线上每个位置作为末尾是否完全匹配第一个串的状态为$S$,与第一个串kmp到了$x$,与第二个串kmp到了$y$的方案数。
然后直接转移即可。
时间复杂度$O(nm2^{m-c+1}c^2)$。
#include<cstdio> const int P=1000000007; int T,n,m,c,i,j,k,S,x,y,A,B,nxt[9],ta[9][3],tb[9][3],na,nb,U,E; int f[1024][6][6],g[1024][6][6],ans; char a[9],b[9]; inline int id(char x){ if(x=='B')return 0; return x=='W'?1:2; } inline void up(int&x,int y){x+=y;if(x>=P)x-=P;} inline void clear(){for(S=0;S<U;S++)for(x=0;x<c;x++)for(y=0;y<c;y++)g[S][x][y]=0;} inline void copy(){for(S=0;S<U;S++)for(x=0;x<c;x++)for(y=0;y<c;y++)f[S][x][y]=g[S][x][y];} int main(){ scanf("%d%d%d%d",&n,&m,&c,&T); while(T--){ scanf("%s%s",a+1,b+1); for(i=1;i<=c;i++)a[i]=id(a[i]),b[i]=id(b[i]); for(nxt[1]=j=0,i=2;i<=c;nxt[i++]=j){ while(j&&a[j+1]!=a[i])j=nxt[j]; if(a[j+1]==a[i])j++; } for(na=nxt[c],i=0;i<c;i++)for(j=0;j<3;j++){ for(k=i;k&&a[k+1]!=j;k=nxt[k]); if(a[k+1]==j)k++; ta[i][j]=k; } for(nxt[1]=j=0,i=2;i<=c;nxt[i++]=j){ while(j&&b[j+1]!=b[i])j=nxt[j]; if(b[j+1]==b[i])j++; } for(nb=nxt[c],i=0;i<c;i++)for(j=0;j<3;j++){ for(k=i;k&&b[k+1]!=j;k=nxt[k]); if(b[k+1]==j)k++; tb[i][j]=k; } U=1<<(m-c+1); for(S=0;S<U;S++)for(x=0;x<c;x++)for(y=0;y<c;y++)f[S][x][y]=0; for(f[0][0][0]=i=1;i<=n;i++){ clear(); for(S=0;S<U;S++)for(x=0;x<c;x++)for(y=0;y<c;y++)if(f[S][x][y])up(g[S][0][0],f[S][x][y]); copy(); for(j=1;j<=m;j++){ clear(); for(S=0;S<U;S++)for(x=0;x<c;x++)for(y=0;y<c;y++)if(f[S][x][y])for(k=0;k<3;k++){ E=S; if(j>=c)if(S>>(j-c)&1)E^=1<<(j-c); A=ta[x][k]; if(A==c)E|=1<<(j-c),A=na; B=tb[y][k]; if(B==c){ if(S>>(j-c)&1)continue; B=nb; } up(g[E][A][B],f[S][x][y]); } copy(); } } for(ans=1,i=n*m;i;i--)ans=3LL*ans%P; for(S=0;S<U;S++)for(x=0;x<c;x++)for(y=0;y<c;y++)up(ans,P-f[S][x][y]); printf("%d\n",ans); } return 0; }