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

集合选数

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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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 1000000001LL

int 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<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<for(int k=0;k<(1<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<return 0;
}

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

你可能感兴趣的:(动态规划,bzoj,HNOI,状压DP,动态规划,oi)