集合选数题解
集合选数题解
构造神题,
对于每一个不含质因数2和3的数字,
我们构造一个矩阵(可能并不满),
第一行第一列是这个数,
在同一行中,下一列的数是这一列的数的2倍,
在同一列中,下一行的数是这一行的数的3倍。
如果我们选矩阵中一个数,
则右边的数是这个数的两倍而不能选,
同理,下面的数同样不能选,
不能选左边的数,因为这个数是它的两倍,
不能选上面的数,因为这个数是它的三倍,
总结一下:
选了一个数就不能选相邻的数,
再一看,列数<=\(log_{2}(n)<17\),行数<=\(log_{3}(n)<11\)
所以,状压啦:
每个不同的矩阵互不影响,根据乘法原理:每次得到的方案数之积即为答案。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=4098,M=20,K=100006;
const ll mod=1e9+1;
int n,p,flag[N],book[K],num[M],f[M][N];
ll ans=1,sum;
inline int read(){
int T=0,F=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
return F*T;
}
void build(int x){
p=1,sum=0; memset(f,0,sizeof(f)); memset(num,0,sizeof(num));
for(int i=x;i<=n;i<<=1,++p) for(int j=i;j<=n;j*=3) ++num[p],book[j]=1;
for(int i=0;i<(1<<num[1]);++i) f[1][i]=flag[i];
for(int i=2;i<p;++i)
for(int j=0;j<(1<<num[i-1]);++j)
for(int k=0;k<(1<<num[i]);++k)
if(flag[j]&&flag[k]&&!(j&k)) f[i][k]=(f[i][k]+f[i-1][j])%mod;
for(int i=0;i<(1<<num[p-1]);++i) sum=(sum+f[p-1][i])%mod;
ans=ans*sum%mod;
}
int main(){
n=read();
for(int i=0;i<=2048;++i) flag[i]=((i<<1)&i)?0:1;
for(int i=1;i<=n;++i) if(!book[i]) build(i);
printf("%lld\n",ans);
return 0;
}