集合选数题解

集合选数题解

构造神题,

对于每一个不含质因数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;
}
posted @ 2019-08-08 19:56  lsoi_ljk123  阅读(129)  评论(0编辑  收藏  举报