【bzoj2734】【HNOI2012】【状压DP】集合选数

来源:互联网 发布:淘宝新开店铺采集器 编辑:程序博客网 时间:2024/04/24 02:17

集合选数

Time Limit: 10 Sec Memory Limit: 128 MB

Description

《集合论与图论》这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,…, n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就 交给你了。

Input

只有一行,其中有一个正整数 n,30%的数据满足 n≤20。

Output

仅包含一个正整数,表示{1, 2,…, n}有多少个满足上述约束条件 的子集。

Sample Input

4

Sample Output

8

样例解释

有8个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。

这道题不好想,但我们可以先从具体的数开始思考,如果两个数分解后因子除去2和3还有差异的话这两个数就是没有关系的,而我们又可以把有关系的数放在一个矩阵里面,如1放在左上角,就成了
1392781
261854162
41236108364
82472216728
然后我们发现这个矩阵的列数是小于等于11的,我们就可以跑一个状压DP,保证没有数相邻就可以了
然后其他在这个矩阵里面没有的数,就再建几个矩阵跑就可以了

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<set>#include<map>#include<queue>#include<algorithm>#include<vector>#include<cstdlib>#include<cmath>#include<ctime>#include<stack>#define INF 2100000000#define ll long long#define clr(x)  memset(x,0,sizeof(x))#define maxclr(x)  memset(x,127,sizeof(x))using namespace std;#define M 100005#define P 1000000001LLint dp[18][2048],a[18][12],f[18],g[16];int pri[10000],mark[M],pt,ans=1;int s[M],n;int get_DP(int x){    a[1][1]=x;    int y=0;clr(f);    for(int i=1;i<=18;i++)    {        if(i!=1)            if(a[i-1][1]*2<=n)            {                a[i][1]=a[i-1][1]*2;                mark[a[i][1]]=1;            }            else             {                y=i-1;                break;            }        for(int j=2;j<=12;j++)            if(a[i][j-1]*3<=n)            {                a[i][j]=a[i][j-1]*3;                mark[a[i][j]]=1;            }            else             {                g[i]=j-1;                break;            }    }    clr(dp);    for(int i=0;i<(1<<g[1]);i++)        if((i&(i<<1))==0)dp[1][i]=1;    for(int i=2;i<=y+1;i++)    {        for(int j=0;j<(1<<g[i]);j++)            for(int k=0;k<(1<<g[i-1]);k++)                if(((j&k)==0)&&((j&(j<<1))==0))                    dp[i][j]=(dp[i][j]+dp[i-1][k])%P;    }    return dp[y+1][0];}int main(){    freopen("in.txt","r",stdin);    freopen("out.txt","w",stdout);    scanf("%d",&n);    for(int i=1;i<=n;i++)    if(!mark[i])        ans=((ll)ans*(ll)get_DP(i))%P;    cout<<ans;    return 0;}

大概就是这个样子,如果有什么问题,或错误,请在评论区提出,谢谢。

0 0