bzoj2734 [HNOI2012]集合选数

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}。

正解:状压dp。

这道题思路太神了。。如果我在考场上肯定就是O(2^n*n)了。。

我们可以把这些数构造成一个矩阵。

1 3 9 27 ...

2 6 18 54 ...

4 12 36 108 ...

...

然后我们可以发现,我们就是要求一个矩阵中相邻的数不选的方案数,并且我们发现,这个矩阵最多只有17行,11列,于是这题就变成了最裸的状压dp了。

但是我们发现,还有一些数没有在矩阵中出现,那么我们每次找到第一个没有出现的数,把它放到1行1列,重新构造一个矩阵。

因为不同矩阵的元素是互不影响的,那么我们把所有矩阵的方案数相乘就行了。

 

 1 //It is made by wfj_2048~
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <complex>
 5 #include <cstring>
 6 #include <cstdlib>
 7 #include <cstdio>
 8 #include <vector>
 9 #include <cmath>
10 #include <queue>
11 #include <stack>
12 #include <map>
13 #include <set>
14 #define inf (1<<30)
15 #define N (100010)
16 #define il inline
17 #define RG register
18 #define ll long long
19 #define rhl 1000000001
20 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
21 
22 using namespace std;
23 
24 int f[18][1<<18],g[18][18],bin[20],C[20],vis[N],n,r,c,res;
25 ll ans;
26 
27 il int gi(){
28     RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
29     if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x;
30 }
31 
32 il void work(){
33     n=gi(),ans=1,bin[0]=1; for (RG int i=1;i<=18;++i) bin[i]=bin[i-1]<<1;
34     for (RG int p=1;p<=n;++p){
35     if (vis[p]) continue; memset(g,0,sizeof(g)); g[1][1]=p,vis[p]=1;
36     for (r=2;g[r-1][1]*2<=n;r++) g[r][1]=g[r-1][1]*2,vis[g[r][1]]=1,C[r]=1; r--;
37     for (c=2;g[1][c-1]*3<=n;c++) g[1][c]=g[1][c-1]*3,vis[g[1][c]]=1; C[1]=--c;
38     for (RG int i=2;i<=r;++i)
39         for (RG int j=2;j<=c && g[i][j-1]*3<=n;++j)
40         g[i][j]=g[i][j-1]*3,vis[g[i][j]]=1,C[i]=j;
41     f[0][0]=1,res=0;
42     for (RG int i=1;i<=r;++i)
43         for (RG int j=0;j<bin[C[i]];++j){
44         f[i][j]=0; if (j&(j<<1) || j&(j>>1)) continue;
45         for (RG int k=0;k<bin[C[i-1]];++k)
46             if (!(j&k)) (f[i][j]+=f[i-1][k])%=rhl;
47         }
48     for (RG int i=0;i<bin[C[r]];++i) (res+=f[r][i])%=rhl; (ans*=(ll)res)%=rhl;
49     }
50     printf("%lld\n",ans); return;
51 }
52 
53 int main(){
54     File("set");
55     work();
56     return 0;
57 }

 

posted @ 2017-03-06 11:53  wfj_2048  阅读(151)  评论(0编辑  收藏  举报