您的位置:首页 > 其它

bzoj2734[HNOI2012]集合选数 状压DP

2017-02-21 12:21 239 查看
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}。

HINT

Source

day2

表示一脸不可做..连图的话复杂度爆炸,只能膜一发题解.

真的强..

因为选了一个数x,不能选2x,3x.那么我们可以建一个矩阵.

x 2x 4x ….

3x 6x 12x

9x 18x 36x

向下乘3,向左乘2.

那么我们只要状压dp一下,相邻不能选的方案数,就可以了,矩阵最多18列11行,不会超.

但是一个不能包括所有,所以要有多个矩阵,不同矩阵之间的数可以同时出现,所以我们可以用乘法原理直接乘就可以了.

dp的时候判断左右相邻不同用(y&y>>1==0),上下相邻用(x&y==0),x是上一行的状态。

具体细节看代码巴。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
#include<queue>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int mo=1e9+1;
typedef long long ll;
ll tot=1;
int bin[20],n,a[20][20],b[20],f[20][2048];
bool bz[100005];
inline int cal(int x)
{
memset(b,0,sizeof(b));
a[1][1]=x;
fo(i,2,18)
if (a[i-1][1]*2<=n)
a[i][1]=a[i-1][1]*2;
else a[i][1]=n+1;
fo(i,1,18)
fo(j,2,11)
if (a[i][j-1]*3<=n)
a[i][j]=a[i][j-1]*3;
else a[i][j]=n+1;
fo(i,1,18)
fo(j,1,11)
if (a[i][j]<=n)
{
bz[a[i][j]]=1;
b[i]+=bin[j-1];
}
fo(i,0,18)
fo(j,0,b[i])
f[i][j]=0;
f[0][0]=1;
fo(i,0,17)
fo(x,0,b[i])
{
if (f[i][x])
fo(y,0,b[i+1])
if (((x&y)==0)&&((y&(y>>1))==0))
f[i+1][y]=(f[i][x]+f[i+1][y])%mo;
}
return f[18][0];
}
int main()
{
scanf("%d",&n);
bin[0]=1;
fo(i,1,19)bin[i]=bin[i-1]<<1;
fo(i,1,n)
if (!bz[i])tot=(tot*cal(i))%mo;
printf("%lld\n",tot);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: