[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

day2

我们可以构造形如以下的一个矩阵

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;
}
时间: 2024-08-09 14:33:42

[HNOI2012][BZOJ2734] 集合选数|状态压缩动态规划|思路题的相关文章

bzoj2734【HNOI2012】集合选数

2734: [HNOI2012]集合选数 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 831  Solved: 487 [Submit][Status][Discuss] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数

【BZOJ1087】【SCOI2005】互不侵犯King 状态压缩 动态规划 水题 都不用加特技

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44022265"); } 题解: 一开始让我写这道题,其实我是,是接受的. BalaBala. 毕竟水题,都不用特技. 裸状压DP. 直接f[i][j][k]表示第i行状态时j,有k个落子时的方案数. 代码: #include <

BZOJ 2734 集合选数(状态压缩DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2734 题意:给出一个由1到n的数字组成的集合.定义合法子集为若x在子集中则2x.3x均不能在子集中.求有多少个合法的子集. 思路: 1   3    9 2   6    12 4   12   36 对于上面的矩阵,我们发现就等价于不选相邻数字的方案数.因此枚举每个还没有用到的数字,建立以该数字为左上角的矩阵.接着就是状态压缩DP. int a[N][N]; i64 f[2][1<<

bzoj 2734: [HNOI2012]集合选数 状压DP

2734: [HNOI2012]集合选数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 560  Solved: 321[Submit][Status] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何

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

[HNOI2012]集合选数 题目描述 <集合论与图论>这门课程有一道作业题,要求同学们求出\({1,2,3,4,5}\)的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中. 同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数, 如何求出\({1,2,3...n}\) 的满足上述约束条件的子集的个数(只需输出对 \(10^{9}+1\) 取模的结果),现在这个问题就交给你了. 输入格式: 只有一行,其中有一个正整数 \(n\) 30

状态压缩动态规划 -- 旅行商问题

旅行商问题: N个点(N<16)的带权有向图D,求一条路径,使得这条路经过每个点恰好一次, 并且路径上边的权值和最小(或者最大),或者求一条具有这样性质的回路. 状态压缩: 将二进制表示十进制数N的点集,比如: 10 = 0000000000001010 代表第1和3个点已经路过 18 = 0000000000010010 代表第1和4个点已经路过 一个整数就是一个点集, dp_arr[binary][to_]代表经过点集 binary 中,当前终点为to_, 且路径最短的值,若该状态不存在就是

状态压缩-动态规划

状态压缩-动态规划 ---By蒟蒻鱼 用二进制表示城市的到达(规划的)状态 每一个二进制数都代表一个唯一的十进制数 预备知识 位运算 优先级 ~ << 和 >> & ^ | 按位与 & 全一则一,否则为零 按位或 | 有一则一,否则为零 按位取反 ~ 是零则一,是一为零 按位异或 ^ 不同则一,相同为零 移位 >> << 基本运算: 集合取并 A|B 集合取交 A&B 集合相减 A&~B 集合相减 ALLBITS~A 置位 A

状态压缩动态规划总结

状态压缩是一个很广的概念,在OI中也有很多的应用,当我们把他应用到动态规划中,可以用来精简状态,节约空间,也方便转移.最常见的就是用二进制来表是状态,利用各种位移运算,就可以实现\(O(1)\)的转移.状压DP适用于“窄棋盘”上的DP,否则状态太多无法存下. POJ1185 炮兵阵地 题意:给一个\(N \times M\)的地形盘,有平原和山坡,要求在平原上部署尽量多的炮(攻击范围2),使得不互相攻击. 数据范围:N <= 100:M <= 10,符合条件.如何表示状态?按行DP,一个二进制

【BZOJ-2732】集合选数 状压DP (思路题)

2734: [HNOI2012]集合选数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1070  Solved: 623[Submit][Status][Discuss] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n