zoukankan      html  css  js  c++  java
  • [HNOI2012][BZOJ2734] 集合选数|状态压缩动态规划|思路题

    2734: [HNOI2012]集合选数

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 708  Solved: 414
    [Submit][Status][Discuss]

    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}。

    HINT

     

    Source

     
    我们可以构造形如以下的一个矩阵
    x 3x 9x 27x...
    2x 6x 18x 54x
    4x 12x 36x 108x
    8x 24x 72x 216x
    就是这种形式
     
    那我们先令x=1吧,构造之:
    1 3 9 27...
    2 6 18 54...
    4 12 36 108...
    8 24 72 216...
    ....................
    我们可以观察到,每个数和他相邻的数都不可同时取,可以计算出本矩阵中取数的方案数。
    但是好像又漏了一些,比如在构造的第一个矩阵中,5和2*5,3*5都没有计算到。
    这时我们又要构造如下一个矩阵
    5 15 45 135...
    10 30 90 270...
    20 60 180 540...
    ........................
    我们又可以计算出本矩阵中取数的方案数。
    再回头看第一个矩阵,7好像也没有取到。
    我们就再构造一个矩阵
    7 21 63 189...
    14 42 126 378...
    28 84 252 756...
    .......................
    以此类推。
    计算出所有矩阵的结果,因为不同矩阵间的数是一定可以共同存在的,此时乘法原理,将各矩阵求得的方案数相乘取模即为答案。
     
    好像忽略了一个问题:怎么统计方案数?
    状压dp。
    f[i][j]表示当前处理到第i行,本行的状态为j。那么看一下j&(j>>1),j&k(k为上一行的某状态)是否都为0,如果是那么就从f[i-1][k]转移而来。
    f[i][j]=sigma(f[i-1][k]|k is ok)。
    至此,本题结束。
     
    #include<bits/stdc++.h>
    #define M 1000000001
    #define ll long long
    using namespace std;
    ll ans=1;
    int n;
    int a[20][20],b[20],f[20][2049];
    bool mark[100005];
    inline int read()
    {
        int a=0,f=1; char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
        while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
        return a*f;
    }
    inline int calc(int x)
    {
        memset(b,0,sizeof(b));
        a[1][1]=x;
        for (int i=2;i<=18;i++)
            if (a[i-1][1]*2<=n) a[i][1]=a[i-1][1]*2; else a[i][1]=n+1;
        for (int i=1;i<=18;i++)
            for (int j=2;j<=11;j++)
                if (a[i][j-1]*3<=n) a[i][j]=a[i][j-1]*3; else a[i][j]=n+1;
        for (int i=1;i<=18;i++)
            for (int j=1;j<=11;j++)
                if (a[i][j]<=n)
                {
                    b[i]+=(1<<(j-1));
                    mark[a[i][j]]=1;
                }
        for (int i=0;i<=18;i++)    
            for (int j=0;j<=b[i];j++)
                f[i][j]=0;
        f[0][0]=1;
        for (int i=0;i<18;i++)    
            for (int j=0;j<=b[i];j++)
                if (f[i][j])
                    for (int k=0;k<=b[i+1];k++)
                        if (((j&k)==0)&&((k&(k>>1))==0))
                            f[i+1][k]=(f[i][j]+f[i+1][k])%M;
        return f[18][0];
    }
    int main()
    {
        n=read();
        for (int i=1;i<=n;i++)
            if (!mark[i]) ans=(ans*calc(i))%M;
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    数据结构与算法分析
    案例分析作业
    实验六——循环结构程序练习总结
    实验五——循环结构学习总结
    实验四——多分支结构及本章总结
    实验三——for 语句及分支结构else-if
    第二次作业及总结——数据类型和运算符
    2-c语言作业
    自然博物馆参观
    2019-2020-1学期 20192409《网络空间安全专业导论》第四周学习总结
  • 原文地址:https://www.cnblogs.com/ws-fqk/p/4764355.html
Copyright © 2011-2022 走看看