状压DP BZOJ2734 [HNOI2012]集合选数

2734: [HNOI2012]集合选数

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1388  Solved: 820
[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}。

写出如下矩阵

1 3  9  27…

2 6 18 54…

4 12 36 108…

发现最多有11列,我们在其中选取一些数,相邻的不能选择,然后就可以状压求方案数了;

但是5没有出现,同样5的倍数也没有出现,7也如此……应该记录哪些数字出现过,没出现过就作为矩阵的第一个元素,最后把若干个矩阵的方案相乘即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const long long mod=1000000001;
 7 long long ans;
 8 int n;
 9 int st[20],jz[20][20],g[20],f[20][5000];
10 bool check[100010];
11 int work(int x){
12     memset(g,0,sizeof(g));
13     jz[1][1]=x;
14     for(int i=2;i<=18;i++)
15         if(jz[i-1][1]*2<=n) jz[i][1]=jz[i-1][1]*2;
16         else jz[i][1]=n+1;
17     for(int i=1;i<=18;i++)
18         for(int j=2;j<=18;j++)
19             if(jz[i][j-1]*3<=n) jz[i][j]=jz[i][j-1]*3;
20             else jz[i][j]=n+1;
21     for(int i=1;i<=18;i++)
22         for(int j=1;j<=18;j++)
23             if(jz[i][j]<=n){
24                 check[jz[i][j]]=1;
25                 g[i]+=st[j-1];
26             }
27     for(int i=0;i<=18;i++)
28         for(int j=0;j<=g[i];j++) f[i][j]=0;
29     f[0][0]=1;
30     for(int i=0;i<18;i++)
31         for(int j=0;j<=g[i];j++)
32             if(f[i][j])
33                 for(int k=0;k<=g[i+1];k++)
34                     if(!(j&k)&&!(k&(k>>1)))    f[i+1][k]=(f[i][j]+f[i+1][k])%mod;
35     return f[18][0];
36 } 
37 int main(){
38     scanf("%d",&n);
39     st[0]=1;
40     for(int i=1;i<=19;i++) st[i]=st[i-1]<<1;
41     ans=1;
42     for(int i=1;i<=n;i++)
43         if(!check[i]) ans=(ans*(long long)work(i))%mod;
44     printf("%lld",ans);
45     return 0;
46 }
原文地址:https://www.cnblogs.com/zwube/p/7261088.html