zoukankan      html  css  js  c++  java
  • bzoj 2734: [HNOI2012]集合选数

    题目描述

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

    同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n<=100000,如何求出{1, 2,..., n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就 交给你了。

    输入输出格式

    输入格式:

    只有一行,其中有一个正整数 n,30%的数据满足 n<=20。

    输出格式:

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

    输入输出样例

    输入样例#1:
    4
    输出样例#1:
    8
     
    【样例解释】 
     
    有8 个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。


    题解:
    这题很有意思,首先你得想到画出所有的倍数表,然后再发现规律......
    如:
    1 3 9
    2 6 18
    4 12 36
    8 24 72
    16 48 144
    32 96 288

    大概是这样横着是乘三,竖着乘二
    这样画出来就发现题目要求的就是所选的数不能相邻......且行列都是log的所以可以直接状压dp
    设f[i][j] 表示前i行,第j行状态为j 那么判断一下直接转移就是了
    注意状压的要是乘三的,状态比乘二的少很多.
    做到这样发现这个矩阵并没有包括所有的数字
    所以还需要找到一个没出现的数继续构造矩阵并dp统计
    如没出现的7
    7 21 63.....
    14 42 126...
    继续构造即可
    最后答案统计时相乘即可
     1 #include <algorithm>
     2 #include <iostream>
     3 #include <cstdlib>
     4 #include <cstring>
     5 #include <cstdio>
     6 #include <cmath>
     7 #define RG register
     8 #define il inline
     9 using namespace std;
    10 typedef long long ll;
    11 const int N=1<<12,M=100005,mod=1000000001;
    12 int f[25][N],lim,sz[N];
    13 bool vis[M];
    14 il int deal(int sta){
    15     int n=0,s=sta,t=sta;
    16     for(int i=1;i<=20;i++){
    17         if(s>lim){
    18             n=i-1;
    19             break;
    20         }
    21         vis[s]=true;t=s;
    22         sz[i]=0;
    23         for(int j=1;j<=15;j++){
    24             if(t>lim)break;
    25             vis[t]=true;
    26             sz[i]++;
    27             t=(t<<1)+t;
    28         }
    29         s<<=1;
    30     }
    31     f[0][0]=1;
    32     for(int i=1;i<=n;i++){
    33         int tot=(1<<sz[i])-1;
    34         for(RG int j=0;j<=tot;j++){
    35             if((j<<1)&j)continue;
    36             int tmp=(1<<sz[i-1])-1;
    37             f[i][j]=0;
    38             for(RG int k=0;k<=tmp;k++){
    39                 if((k<<1)&k)continue;
    40                 if(j&k)continue;
    41                 f[i][j]+=f[i-1][k];
    42                 if(f[i][j]>=mod)f[i][j]-=mod;
    43             }
    44         }
    45     }
    46     int tot=(1<<sz[n])-1,ret=0;
    47     for(RG int j=0;j<=tot;j++){
    48         if(j&(j<<1))continue;
    49         ret+=f[n][j];if(ret>=mod)ret-=mod;
    50     }
    51     return ret;
    52 }
    53 void work()
    54 {
    55     scanf("%d",&lim);
    56     ll ans=1;
    57     for(int i=1;i<=lim;i++){
    58         if(!vis[i])
    59             ans*=deal(i),ans%=mod;
    60     }
    61     printf("%lld
    ",ans);
    62 }
    63 
    64 int main()
    65 {
    66     work();
    67     return 0;
    68 }
    
    
    
     
  • 相关阅读:
    RabbitMQ系列教程之一:我们从最简单的事情开始!Hello World(转载)
    如何安装和配置RabbitMQ(转载)
    C++学习笔记-模板
    C++学习笔记-多态的实现原理
    C++学习笔记-多态的实现原理
    C++学习笔记-多态
    C++学习笔记-多态
    C++学习笔记-继承中的构造与析构
    C++学习笔记-继承
    C++学习笔记-封装
  • 原文地址:https://www.cnblogs.com/Yuzao/p/7382800.html
Copyright © 2011-2022 走看看