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 }