您的位置:首页 > 其它

bzoj2734 [HNOI2012]集合选数

2017-03-06 11:53 344 查看

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列,重新构造一个矩阵。

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

//It is made by wfj_2048~
#include <algorithm>
#include <iostream>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define inf (1<<30)
#define N (100010)
#define il inline
#define RG register
#define ll long long
#define rhl 1000000001
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)

using namespace std;

int f[18][1<<18],g[18][18],bin[20],C[20],vis
,n,r,c,res;
ll ans;

il int gi(){
RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x;
}

il void work(){
n=gi(),ans=1,bin[0]=1; for (RG int i=1;i<=18;++i) bin[i]=bin[i-1]<<1;
for (RG int p=1;p<=n;++p){
if (vis[p]) continue; memset(g,0,sizeof(g)); g[1][1]=p,vis[p]=1;
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--;
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;
for (RG int i=2;i<=r;++i)
for (RG int j=2;j<=c && g[i][j-1]*3<=n;++j)
g[i][j]=g[i][j-1]*3,vis[g[i][j]]=1,C[i]=j;
f[0][0]=1,res=0;
for (RG int i=1;i<=r;++i)
for (RG int j=0;j<bin[C[i]];++j){
f[i][j]=0; if (j&(j<<1) || j&(j>>1)) continue;
for (RG int k=0;k<bin[C[i-1]];++k)
if (!(j&k)) (f[i][j]+=f[i-1][k])%=rhl;
}
for (RG int i=0;i<bin[C[r]];++i) (res+=f[r][i])%=rhl; (ans*=(ll)res)%=rhl;
}
printf("%lld\n",ans); return;
}

int main(){
File("set");
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: