zoukankan      html  css  js  c++  java
  • [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-n分成若干组,其中每一组相互独立,第i组可以表示成形如
    x 2x 4x 8x
    3x 6x 12x 24x
    9x 18x 36x 72x
    这样随着行数递增列数单调不增的少一块的矩阵。
    满足条件的方案就是矩阵中不能选出相邻的数。

    因为每一组相互独立,所以可以将每一组的答案乘起来,得到最后的答案。
    所以接下来看一下如何求一组的答案。

    发现行数最多是10(3^11>10^5),所以可以把一列作为状态进行转移,
    需要注意的是随着枚举的列编号增大,一列的行数是可能会减少的,这时候需要判定一下。

    细节比较多(我的代码是把*3横着放所以一行一个状态),但是我的代码还算比较快的(bzoj rank 40/9??)。

    /**************************************************************
        Problem: 2734
        User: JYYHH
        Language: C++
        Result: Accepted
        Time:28 ms
        Memory:1408 kb
    ****************************************************************/
     
    #include<bits/stdc++.h>
    #define ll long long
    #define maxn 100005
    #define ha 1000000001
    using namespace std;
    int ci[20],n,m;
    int zt[1005],num=0;
    bool v[maxn];
    int f[20][250];
    ll ans=1;
     
    inline void init(){
        ci[0]=1;
        for(int i=1;i<=15;i++) ci[i]=ci[i-1]+ci[i-1];
    }
     
    inline ll solve(int pos){
        ll an=0; int l=0,h=0;
         
        for(int i=pos;i<=n;i<<=1)
            for(int j=i;j<=n;j*=3) v[j]=1;
        for(int i=pos;i<=n;i*=3,l++);
         
        num=0,f[0][1]=1;
        for(int i=0;i<ci[l];i++) if(!(i&(i<<1))) zt[++num]=i;
         
        h=1;
        for(;pos<=n;pos<<=1,h++){
            l=0;
            for(int j=pos;j<=n;j*=3,l++);
             
            for(int j=1;j<=num&&zt[j]<ci[l];j++)
                for(int k=1;k<=num;k++) if(!(zt[j]&zt[k])){
                    f[h][j]+=f[h-1][k];
                    if(f[h][j]>=ha) f[h][j]-=ha;
                }
        }
        h--;        
        for(int j=1;j<=num;j++){
            an+=f[h][j];
            if(an>=ha) an-=ha; 
        }
         
        for(int i=1;i<=h;i++)
            for(int j=1;j<=num;j++) f[i][j]=0;
         
        return an;
    }
     
    int main(){
        init();
        scanf("%d",&n);
         
        for(int i=1;i<=n;i++) if(!v[i]){
            ans=ans*solve(i);
            if(ans>=ha) ans-=ans/ha*ha;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
     
  • 相关阅读:
    Java+seleinum+testng框架UI自动化测试环境搭建--第一节
    linux常用命令
    Linux下定时任务的查看及取消
    WampServer环境安装
    Airtest断言方法
    Pycharm创建模板头部默认
    QPS/TPS简介
    简易全文搜索引擎设计
    如何在linux下检测内存泄漏
    箱线图(boxplot)简介与举例
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8313407.html
Copyright © 2011-2022 走看看