• 【状压DP】BZOJ2734-[HNOI2012]集合选数


    已经八月份了药丸,开始肝作业并且准备高考啦!!

    【题目大意】

    《集合论与图论》这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。现在求以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,..., n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果)(包括空集)。

    【思路】

    对于n以内任意与6互质的整数x,我们列出一个矩阵:

    x 3x 9x 27x ...

    2x 6x 18x 54x ...

    4x 12x 36x 108x ...

    所以我们现在枚举与6互质的这个数x,然后进行状态压缩的转移。这个有点类似于先前的king。f[i][j]表示到第i行,且第i行状态为j的总可能性。不过它并不一定是矩形,每一行的列数可能不同,对于某行列数为j,我们只需枚举0..2^j-1的状态,并记录为before转移到下一行DP。

    这里用了滚动数组,不过不要忘记每次新的滚动数组都要清空一下。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define mod 1000000001
     6 using namespace std;
     7 typedef long long LL;
     8 const int MAXN=12;
     9 int n;
    10 int usable[1<<MAXN],f[2][1<<MAXN];
    11 bool mark[1<<MAXN];
    12 
    13 int Usable(int x)
    14 {
    15     if (x<<1&x || x>>1&x) return 0;else return 1;
    16 }
    17 
    18 int dp(int now)
    19 {
    20     memset(f,0,sizeof(f));
    21     int cur=0,before=-1;//before指上一行有几个数 
    22     for (int i=0;now*(1<<i)<=n;i++)//枚举每一行的第一个数,求出总的行数 
    23     {
    24         cur=1-cur;
    25         int tmp=now*(1<<i),j;
    26         for (j=0;tmp<=n;j++,tmp*=3);//求出每一行有几个数
    27         for (int k=0;k<(1<<j);k++)//枚举当前行的状态 
    28         {
    29             f[cur][k]=0;//【不要忘记了初始化☆】 
    30             if (usable[k])
    31             {
    32                 if (before==-1) {f[cur][k]=1;continue;}//如果是第一行,则将可行状态设为1 
    33                 for (int p=0;p<(1<<before);p++)
    34                     if (usable[p])
    35                         if ((k&p)==0) f[cur][k]=f[cur][k]+f[1-cur][p],f[cur][k]%=mod;//这里不要忘记了也要mod 
    36             }
    37         }
    38         before=j;
    39     }
    40     int ans=0;
    41     for (int i=0;i<(1<<before);i++) ans+=f[cur][i],ans%=mod; 
    42     return (ans);
    43 }
    44 
    45 void getusable()
    46 {
    47     memset(usable,0,sizeof(usable));
    48     for (int i=0;i<(2<<MAXN);i++)
    49         if (Usable(i)) usable[i]=1;
    50 }
    51 
    52 void solve()
    53 {
    54     memset(mark,0,sizeof(mark));
    55     LL ans=1;//为了防止乘法的时候溢出,可以先用longlong,再转换回int 
    56     for (int i=1;i<=n;i++)
    57         if ((i%2)&&(i%3)) ans=(ans*dp(i))%mod;
    58     printf("%d
    ",(int)ans);
    59 }
    60 
    61 int main()
    62 {
    63     scanf("%d",&n);
    64     getusable();
    65     solve();
    66     return 0;
    67 }
  • 相关阅读:
    mentohust 使用
    查找 GPU 计算能力
    在写代码过程中遇到的问题,以及当时的解决方法(如实记录)
    ubuntu14.04 解决屏幕亮度无法调节的问题
    Ubuntu14.04下安装 boost (boost_1.54 最简单的方法)
    在 Ubuntu下安装 labelImg (标数据用)
    在树莓派上配置MariaDB
    使用Telegraf + Influxdb + Grafana 监控SQLserver服务器的运行状况
    如何读懂SQL Server的事务日志
    ActiveMQ安装使用与spring整合配置教程
  • 原文地址:https://www.cnblogs.com/iiyiyi/p/5726420.html
Copyright © 2020-2023  润新知