zoukankan      html  css  js  c++  java
  • [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

     
    我们可以构造形如以下的一个矩阵
    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;
    }
  • 相关阅读:
    LeetCode Count of Range Sum
    LeetCode 158. Read N Characters Given Read4 II
    LeetCode 157. Read N Characters Given Read4
    LeetCode 317. Shortest Distance from All Buildings
    LeetCode Smallest Rectangle Enclosing Black Pixels
    LeetCode 315. Count of Smaller Numbers After Self
    LeetCode 332. Reconstruct Itinerary
    LeetCode 310. Minimum Height Trees
    LeetCode 163. Missing Ranges
    LeetCode Verify Preorder Serialization of a Binary Tree
  • 原文地址:https://www.cnblogs.com/ws-fqk/p/4764355.html
Copyright © 2011-2022 走看看