zoukankan      html  css  js  c++  java
  • [HNOI2012]集合选数 --- 状压DP

    [HNOI2012]集合选数

    题目描述

    《集合论与图论》这门课程有一道作业题,要求同学们求出({1,2,3,4,5})的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。

    同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数,

    如何求出({1,2,3...n}) 的满足上述约束条件的子集的个数(只需输出对 (10^{9}+1) 取模的结果),现在这个问题就交给你了。

     

    输入格式:

    只有一行,其中有一个正整数 (n)

    30%的数据满足 (n<=20)。

    100%数据满足 (n<=100000)。

    输出格式:

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

     

    输入样例#1: 
    4
     
    输出样例#1: 
    8

    样例解释
    有8 个集合满足要求,
    分别是空集,({1},{1,4},{2},{2,3},{3},{3,4},{4})。
     
    很妙的一道题。
    不妨把2的倍数写作一行,3的倍数写作一列。
    比如下面这个矩阵

    egin{bmatrix}
    &1 &2 &4 &8 ;;\
    &3 &6 &12 &24 ;;\
    &9 &18 &36 &72 ;;
    end{bmatrix}

    可以发现,一行的元素数和列数都不会超过(log n)
    因此,考虑状压行列来(DP)
     
    如果这个矩阵中没有涉及到的元素呢?
    重新新设一个矩阵来(DP)
     
    补充两个数:
    (log_{2} n = 18)
    (log_{3} n = 11)
    最终复杂度为(O(2^{22}*18))(可能不准确)
     
    注意:
    状压的那一维一定是3的倍数的那一维,否则复杂度将退化至(O(11*2^{36}))(喜闻乐见的我)
    模数是(10^{9}+1),不要打错(没错,又是我)
     
    #include <cstdio>
    #include <cstring>
    #include <bitset>
    #define mod (100000001)
    #define sid 200050
    #define ri register int
    using namespace std;
    
    inline void up(int &x, int y) {
        x += y; if(x >= mod) x -= mod;
    }
    
    inline void mu(int &x, int y) {
        long long tmp = (1ll * x * y) % mod;
        x = (int)tmp;
    }
    
    int n;
    bitset <100050> flag;
    int num[25][25], lim[40], bit[40];
    int dp[20][sid], ans = 1;
    
    inline int Solve(int kp) {
        memset(num, 0, sizeof(num));
        num[1][1] = kp; 
        for(ri i = 1; i <= 19; i ++) lim[i] = 0;
        for(ri i = 2; i <= 11; i ++) 
        if(num[1][i - 1] * 3 <= n) num[1][i] = num[1][i - 1] * 3;
        for(ri i = 2; i <= 18; i ++) {
            if(num[i - 1][1] * 2 > n) break;
            num[i][1] = num[i - 1][1] * 2;
            for(ri j = 2; j <= 11; j ++) 
            if(num[i][j - 1] * 3 <= n) num[i][j] = num[i][j - 1] * 3;
        }
        for(ri i = 1; i <= 18; i ++)
        for(ri j = 1; j <= 11; j ++)
        if(num[i][j]) lim[i] |= bit[j - 1], flag[num[i][j]] = 1;
        for(ri i = 1; i <= 19; i ++)
        for(ri j = 0; j <= lim[i]; j ++) dp[i][j] = 0;
        dp[0][0] = 1;
        for(ri i = 1; i <= 19; i ++)
        for(ri j = 0; j <= lim[i - 1]; j ++)
        if(dp[i - 1][j])
        for(ri k = 0; k <= lim[i]; k ++)
        if((k & (k >> 1)) == 0 && (j & k) == 0)
        up(dp[i][k], dp[i - 1][j]);
        return dp[19][0];
    }
    
    inline void DP() {
        for(ri i = 0; i <= 27; i ++) bit[i] = 1 << i;
        for(ri i = 1; i <= n; i ++) if(!flag[i]) mu(ans, Solve(i));
        printf("%d
    ", ans);
    }
    
    int main() {
        scanf("%d", &n);
        DP();
        return 0;
    }
    sad
  • 相关阅读:
    转:WinCE驱动开发问题精华集锦
    转:WINCE6.0+S3C6410下的DM9000A驱动
    转:WinCE6.0 不重启修改IP地址
    前端小功能:canvas验证码
    前端小功能:canvas签名版
    JavaScript中数组的应用方式
    ES6--Promise
    wepy 语法高亮
    Javascript和JQuery获取浏览器窗口各种尺寸
    图片预加载
  • 原文地址:https://www.cnblogs.com/reverymoon/p/8971431.html
Copyright © 2011-2022 走看看