2734: [HNOI2012]集合选数 - BZOJ

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

1

2   3

4   6   9

8   12 18

但是好像不行

看了题解才知道

我们把图弄成这样(往下走是*2,往右走是*3,就变成相邻的数不能选,可以用状压dp)

1   3   9

2   6   18

4   12 36

8   24 72

............

但是要注意这个时候我们并没有把所有的数都考虑到,比如5的倍数,所以我们枚举左上角的数,然后用乘法定理

具体做法是用一个flag存这个数是否考虑过,没考虑就把他当做左上角的数做一遍

 1 const

 2         h=1000000001;

 3         maxn=100010;

 4 var

 5         f:array[0..18,0..2048]of longint;

 6         num:array[0..18]of longint;

 7         flag:array[0..maxn]of boolean;

 8         n:longint;

 9         ans:int64;

10 

11 function get(x:longint):int64;

12 var

13         i,j,k,s,w:longint;

14 begin

15         get:=0;

16         s:=x;

17         w:=x;

18         flag[x]:=true;

19         num[1]:=1;

20         while w*3<=n do

21           begin

22             w:=w*3;

23             flag[w]:=true;

24             inc(num[1]);

25           end;

26         for j:=0 to 1<<(num[1])-1 do

27           if j and(j<<1)=0 then f[1,j]:=1;

28         i:=1;

29         while s*2<=n do

30           begin

31             inc(i);

32             s:=s*2;

33             w:=s;

34             num[i]:=1;

35             flag[w]:=true;

36             while w*3<=n do

37               begin

38                 w:=w*3;

39                 flag[w]:=true;

40                 inc(num[i]);

41               end;

42             for j:=0 to 1<<(num[i])-1 do

43               f[i,j]:=0;

44             for j:=0 to 1<<(num[i])-1 do

45               for k:=0 to 1<<(num[i-1])-1 do

46                 if (j and(j<<1)=0) and (j and k=0) then f[i,j]:=(f[i,j]+f[i-1,k])mod h;

47           end;

48         for j:=0 to 1<<(num[i])-1 do

49           inc(get,f[i,j]);

50 end;

51 

52 procedure main;

53 var

54         i:longint;

55 begin

56         read(n);

57         ans:=1;

58         for i:=1 to n do

59           if flag[i]=false then ans:=(ans*get(i))mod h;

60         writeln(ans);

61 end;

62 

63 begin

64         main;

65 end.
View Code

 

你可能感兴趣的:(2012)