状压$+$构造矩阵
首先,题目中要求对于每个数$x$,不能选出$2x,3x$,我们可以构造出一个矩阵,如下:
$egin{matrix}x & 3x & 9x & 27x & ...\2x & 6x & 18x & 54x & ...\4x & 12x & 36x & 108x & ...\8x & 24x & 72x & 216x & ...\... & ... & ... & ... & ...end{matrix}$
那么显然,当我们取了某个数后,例如取的是$6x$,那么我们就不能取在它右方的$18x$,以及在它下方的$12x$,对于每个矩阵中的数都如此
而在实际上,我们还要弄出一个边界,例如上面矩阵中的$216x$很可能超过$n$,所以对于每一行,我们都要弄出一个边界,所以所谓的矩阵其实是锯齿状的样子
限制条件解决了的话,就开始状压吧
代码:
#include<iostream>
#include<cstdio>
#define int long long
#define mod 1000000001
#define N 100007
using namespace std;
int n,ans=1;
int g[21][21],limit[21],vis[N],f[21][1<<12];
int Get(int x)
{
int maxc=1,maxk=1,chang=x,kuan=x;
while(3*chang<=n)
++maxk,chang*=3;
while(2*kuan<=n)
++maxc,kuan*=2;
g[1][1]=x;vis[x]=1;
for(int i=2;i<=maxk;++i) g[1][i]=g[1][i-1]*3,vis[g[1][i]]=1;
for(int i=2;i<=maxc;++i)
{
g[i][1]=g[i-1][1]*2;
vis[g[i][1]]=1;
for(int j=2;j<=maxk;++j)
{
if(g[i][j-1]*3>n)
break;
g[i][j]=g[i][j-1]*3;
vis[g[i][j]]=1;
}
}
for(int i=1;i<=maxc;++i)
{
int j;
for(j=1;j<=maxk;++j)
if(g[i][j])
g[i][j]=0;
else
break;
for(limit[i]=0;j<=maxk;++j)
limit[i]|=1<<(maxk-j);
}
int S=(1<<maxk)-1;
for(int i=0;i<=S;++i)
if(!(i&(i<<1))&&!(i&(i>>1))&&!(i&limit[1]))
f[1][i]=1;
for(int i=2;i<=maxc;++i)
for(int j=0;j<=S;++j)
if(!(j&(j<<1))&&!(j&(j>>1))&&!(j&limit[i]))
{
f[i][j]=0;
for(int o=0;o<=S;++o)
if(!(o&(o<<1))&&!(o&(o>>1))&&!(o&limit[i-1])&&!(o&j))
f[i][j]=(f[i][j]+f[i-1][o])%mod;//cout<<"sasdasd
";
}
int sum=0;
for(int i=0;i<=S;++i)
if(!(i&(i<<1))&&!(i&(i>>1))&&!(i&limit[maxc]))
sum=(sum+f[maxc][i])%mod;//cout<<sum<<"
";
/* printf("maxc:%d maxk:%d
",maxc,maxk);
for(int i=1;i<=maxc;++i)
{
printf("%d ",limit[i]);
}
printf("
");
for(int i=1;i<=maxc;++i)
{
for(int j=1;j<=maxk;++j)
printf("%d ",g[i][j]);
printf("
");
}*/
return sum;
}
signed main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
if(!vis[i])
ans=(ans*Get(i))%mod;//printf("%d ",ans);
printf("%d",ans);
}