BZOJ_2734_[HNOI2012]集合选数_构造+状压DP

题意:《集合论与图论》这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,..., n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就 交给你了。

分析:

我们构造出一个矩阵

1 2^0*3^1 2^0*3^2
2^1*3^0 2^1*3^1 2^1*3^2
2^2*3^0 2^2*3^1 2^2*3^2

发现矩阵的相邻两个格子的数不能同时取

状压DP一下

要把所有不在矩阵中的数当作1重新构造,比如5,7等等

每个矩阵的结果乘起来就是答案

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL long long
LL p=1000000001,A,f[18][1<<12];
int vis[100050],s[18],mat[18][18];
LL ans=1;
void build(int x){
    int n=1,m=1,now=x;
    while(now*3<=A)m++,now*=3;
    now=x;
    while(now*2<=A)n++,now<<=1;
    int mask=(1<<m)-1;
    memset(s,0,sizeof(s));
    memset(f,0,sizeof(f));
    memset(mat,0,sizeof(mat));
    mat[1][1]=x;vis[x]=1;
    for(int i=2;i<=m;i++){
        mat[1][i]=mat[1][i-1]*3;
        vis[mat[1][i]]=1;
    }
    s[1]=mask;
    for(int i=2;i<=n;i++){
        mat[i][1]=mat[i-1][1]*2;
        vis[mat[i][1]]=1;
        for(int j=2;j<=m;j++){
            mat[i][j]=mat[i-1][j]*2;
            if(mat[i][j]>A){
                s[i]=mask^((1<<m-j+1)-1);
                break;
            }
            vis[mat[i][j]]=1;
        }
        if(!s[i])s[i]=mask;
    }
    f[0][0]=1;
    s[0]=mask;
    for(int i=0;i<n;i++){
        for(int j=0;j<=mask;j++){
            if((j|s[i])!=s[i])continue;
            if(j&(j<<1))continue;
            for(int k=0;k<=mask;k++){
                if((k|s[i+1])!=s[i+1])continue;
                if(k&(k<<1))continue;
                if(j&k)continue;
                f[i+1][k]+=f[i][j];
                f[i+1][k]%=p;
            }
        }
    }
    LL re=0;
    for(int i=0;i<=mask;i++)re+=f[n][i],re%=p;
    ans=re*ans%p;

    /*for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            printf("%d ",mat[i][j]);
        }
        puts("");
    }*/

    /*for(int i=1;i<=n;i++){
        printf("%d\n",s[i]);
    }*/
}
int main(){
    scanf("%lld",&A);
    for(int i=1;i<=A;i++){
        if(!vis[i])build(i);
    }
    printf("%lld",ans);
}

原文地址:https://www.cnblogs.com/suika/p/8506730.html

时间: 2024-11-07 16:31:53

BZOJ_2734_[HNOI2012]集合选数_构造+状压DP的相关文章

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

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

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,如何

[BZOJ2734][HNOI2012]集合选数

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

bzoj 2734: [HNOI2012]集合选数

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

[BZOJ2734][HNOI2012] 集合选数(状态压缩+思维)

Description 题目链接 Solution 可以根据条件构造出一个矩阵, 1 3 9 27 81... 2 6 18.... 4 12 36... 这个矩阵满足\(G[i][1]=G[i-1][1]*2(1< i),G[i][j]=G[i][j-1]*3(1\leq i,1<j)\) 也就是要满足不能同时选择矩阵中\((G[i][j],G[i][j+1],G[i+1][j])\) 而且会发现,矩阵可能有多个,应枚举矩阵的\(G[1][1]\)并记录下出现过的数 这样会发现矩阵最大长为1

HDU 1565&amp;1569 方格取数系列(状压DP或者最大流)

方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6206    Accepted Submission(s): 1975 Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的

[HNOI2012]集合选数

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

HDU 1565 方格取数(1)(状压DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1565 Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大. Input 包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20) Output 对于每个测试实例,输出可能取得的最大的和 Sample Input 3 75

【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