码迷,mamicode.com
首页 > 其他好文 > 详细

[HNOI2012]集合选数 --- 状压DP

时间:2018-04-29 18:35:56      阅读:179      评论:0      收藏:0      [点我收藏+]

标签:cst   问题:   状压   pen   多少   否则   oid   microsoft   isp   

[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的倍数写作一列。
比如下面这个矩阵

\begin{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\n", ans);
}

int main() {
    scanf("%d", &n);
    DP();
    return 0;
}
sad

 

[HNOI2012]集合选数 --- 状压DP

标签:cst   问题:   状压   pen   多少   否则   oid   microsoft   isp   

原文地址:https://www.cnblogs.com/reverymoon/p/8971431.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!