这道题的难点在于思考dp表示什么
首先可以令ans[len]表示白色子矩阵边长最大值大于等于len的方案数则ans[len]-ans[len+1]就是beautifulness为len的方案数
白色子矩阵边长最大值大于等于len的方案数=总方案-白色子矩阵边长最大值小于len的方案数
经过这样的转化,我们就好dp了,我们先穷举len
令f[i][st]表示到第i行,状态为st的白色子矩阵边长最大值小于len的方案数
怎么设计状态呢,由于要保证白色子矩阵边长最大值小于len
我们维护一个n-len+1的len进制数,第j位表示(i,j)~(i,j+len-1)中向上延伸的白色格子数的最小值
这样我们二进制穷举每行的涂色方法就可以转移状态了
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 typedef long long ll; 5 const int mo=1e9+7; 6 int d[10],a[10],ans[10],v[10],n; 7 char s[10]; 8 void inc(int &a,int b) 9 { 10 a+=b; 11 if (a>mo) a-=mo; 12 } 13 14 struct node 15 { 16 int st[200010],c[200010],len; 17 void clr() 18 { 19 for (int i=1; i<=len; i++) c[st[i]]=0; 20 len=0; 21 } 22 void push(int nw,int w) 23 { 24 if (!c[nw]) st[++len]=nw; 25 inc(c[nw],w); 26 } 27 } f[2]; 28 int main() 29 { 30 int cas; 31 scanf("%d",&cas); 32 f[0].len=f[1].len=0; 33 while (cas--) 34 { 35 int m=1; 36 scanf("%d",&n); 37 for (int i=1; i<=n; i++) 38 { 39 scanf("%s",s); 40 a[i]=0; 41 for (int j=0; j<n; j++) 42 if (s[j]=='o') m=m*2%mo; 43 else a[i]|=(1<<j); 44 } 45 ans[0]=1; 46 ans[1]=(m-1+mo)%mo; 47 for (int l=2; l<=n; l++) 48 { 49 int p=0,k=n+1-l; 50 d[0]=1; 51 for (int i=1; i<=n; i++) d[i]=d[i-1]*l; 52 f[0].clr(); 53 f[0].push(0,1); 54 for (int i=1; i<=n; i++) 55 { 56 p^=1; 57 f[p].clr(); 58 for (int cur=0; cur<(1<<n); cur++) 59 { 60 if (cur&a[i]) continue; 61 for (int r=0; r<k; r++) v[r]=0; 62 for (int r=0; r<n; r++) 63 { 64 if ((cur>>r)&1) continue; 65 for (int j=0; j<k; j++) 66 if (r>=j&&r<j+l) v[j]=1; 67 } 68 for (int j=1; j<=f[p^1].len; j++) 69 { 70 int pre=f[p^1].st[j],nw=0; 71 int w=f[p^1].c[pre]; 72 for (int r=0; r<k; r++) 73 { 74 int x=pre%l; pre/=l; 75 if (v[r]) continue; 76 if (x==l-1) {nw=-1; break;} 77 nw+=(x+1)*d[r]; 78 } 79 if (nw>-1) f[p].push(nw,w); 80 } 81 } 82 } 83 ans[l]=0; 84 for (int i=1; i<=f[p].len; i++) 85 { 86 int x=f[p].st[i]; 87 inc(ans[l],f[p].c[x]); 88 } 89 ans[l]=(m-ans[l]+mo)%mo; 90 ans[l-1]=(ans[l-1]-ans[l]+mo)%mo; 91 } 92 for (int i=0; i<=n; i++) 93 printf("%d ",ans[i]); 94 } 95 }