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;
}

浙公网安备 33010602011771号