BZOJ 2734: [HNOI2012]集合选数
我们构造一个矩阵,左上角为1,右边的数为左边的数*3,下边的数为上边的数*2,那么这个矩阵的列数不会超过11,行数不会超过17
对于矩阵中的数,只要选出的两个数的位置不是四联通,就是合法的
状压一行,转移
可能有多个矩阵,即左上角不一定是1
枚举左上角,且保证这个数不被包含在其他矩阵中,那么矩阵之间就没有相同的数,互不影响,乘法原理即可
#include<cstdio> using namespace std; const int mod=1e9+1; int n,A[18][18],F[18][10005],line[18],vis[100005]; int solve(int ST){ int M; A[0][0]=ST; for (M=0; A[M][0]<=n; M++) A[M+1][0]=A[M][0]*2; for (int i=0; i<M; i++) for (line[i]=0; A[i][line[i]]<=n; line[i]++) { vis[A[i][line[i]]]=1; A[i][line[i]+1]=A[i][line[i]]*3; } for (int i=0; i<M; i++) for (int j=0; j<(1<<line[i]); j++) F[i][j]=0; for (int i=0; i<(1<<line[0]); i++) if (!(i&(i>>1))) F[0][i]=1; for (int i=0; i<M-1; i++) for (int pre=0; pre<(1<<line[i]); pre++) if (F[i][pre]) for (int now=0; now<(1<<line[i+1]); now++) if (!(now&(now>>1)) && !(pre&now)) (F[i+1][now]+=F[i][pre])%=mod; int ans=0; for (int i=0; i<(1<<line[M-1]); i++) (ans+=F[M-1][i])%=mod; return ans; } int main(){ scanf("%d",&n); int ans=1; for (int i=1; i<=n; i++) if (!vis[i]) ans=1ll*ans*solve(i)%mod; printf("%d\n",ans); return 0; }