BZOJ2734:[HNOI2012]集合选数

本文介绍了一种高效算法,用于计算特定条件下集合的子集数量。问题来源于《集合论与图论》课程的一道作业题,通过状态压缩动态规划的方法解决了对于任意正整数n,求出{1,2,...,n}

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}。
 
题解:
设x为一个不含2与3因子的数,则可以构造出一个这样的矩阵:
xx*3x*3^2x*3^3...
2*x2*x*32*x*3^22*x*3^3...
2^2*x2^2*x*3 2^2*x*3^22^2*x*3^3...
2^3*x2^3*x*3 2^3*x*3^22^3*x*3^3...
.......
.......
.......
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
例如x=1时:
 1  3  9 27...
   2      6      18      54   ...
   4      12      36      108   ...
82472216...
.......
.......
.......
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
如此一来,不能同时取的数对在矩阵中一定是左右或是上下相邻的。因为n<=1000000,所以矩阵最多有11列,可以将每一行进行状态压缩DP。
每个数一定且只存在于一个矩阵之中,两个矩阵之间没有关联,所以DP出每个矩阵的可行方案数,最后相乘就可以了。
 
代码:
 1 const
 2   mo=1000000001;
 3 var
 4   i,j,k,l,n,m:longint;
 5   a:array[0..20,0..20]of longint;
 6   b:array[0..20]of longint;
 7   f:array[0..20,0..2048]of longint;
 8   bo:array[0..100005]of longint;
 9   bin:array[0..20]of longint;
10   ans:int64;
11 function qq(x:longint):longint;
12 var i,j,y:longint;
13 begin
14   fillchar(b,sizeof(b),0);
15   a[1,1]:=x;
16   for i:=2 to 18 do
17   if a[i-1,1]*2<=n then a[i,1]:=a[i-1,1]*2 else a[i,1]:=n+1;
18   for i:=1 to 18 do
19   for j:=2 to 11 do
20   if a[i,j-1]*3<=n then a[i,j]:=a[i,j-1]*3 else a[i,j]:=n+1;
21   for i:=1 to 18 do
22   for j:=1 to 11 do
23   if a[i,j]<=n then
24   begin
25     b[i]:=b[i]+bin[j-1];
26     bo[a[i,j]]:=1;
27   end;
28   for i:=0 to 18 do
29   for j:=0 to b[i] do f[i,j]:=0;
30   f[0,0]:=1;
31   for i:=0 to 17 do
32   for j:=0 to b[i] do
33   if f[i,j]>0 then
34   for y:=0 to b[i+1] do
35   if(j and y=0)and(y and(y shr 1)=0)then f[i+1,y]:=(f[i+1,y]+f[i,j])mod mo;
36   exit(f[18,0]);
37 end;
38 begin
39   bin[0]:=1;
40   for i:=1 to 20 do bin[i]:=bin[i-1]shl 1;
41   readln(n); ans:=1;
42   for i:=1 to n do
43   if bo[i]=0 then ans:=(ans*qq(i))mod mo;
44   writeln(ans);
45 end.
View Code
 

转载于:https://www.cnblogs.com/GhostReach/p/6290821.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值