[HNOI2012]集合选数
题目描述
《集合论与图论》这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。
同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n<=100000,如何求出{1, 2,..., n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就 交给你了。
输入输出格式
输入格式:只有一行,其中有一个正整数 n,30%的数据满足 n<=20。
输出格式:仅包含一个正整数,表示{1, 2,..., n}有多少个满足上述约束条件 的子集。
输入输出样例
输入样例#1:
复制
输出样例#1: 复制
有8 个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。
对于一个数,我们可以把它分解成$k*2^{i}*3^{j}$
对于一个数,我们可以把它分解成$k*2^{i}*3^{j}$
每个k(不含2,3的因数)可以单独考虑,答案是所有k方案的积
即考虑$k*2^i*3^j<=n$的方案
构造一个矩阵:
k 3k 9k 27k
2k 6k 18k 54k
4k 12k 36k 108k
令f[i][S]表示当前选到2^i,行状态为S
S第j位为1表示选了2^i*3^j
首先根据题意,S相邻2位不能为1
然后转移时前面的状态S'
要满足与S&S'=0
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 int n,Log2[100001],Log3[100001],pw2[21],pw3[21]; 8 int ans,Mod=1e9+1,f[21][100001],s[100001],cnt,p,q,sum; 9 int main() 10 {int i,j,k,l; 11 cin>>n; 12 Log2[1]=0; 13 for (i=2;i<=n;i++) 14 Log2[i]=Log2[i/2]+1; 15 Log3[1]=0; 16 for (i=3;i<=n;i++) 17 Log3[i]=Log3[i/3]+1; 18 pw2[0]=1; 19 for (i=1;i<=18;i++) 20 pw2[i]=pw2[i-1]*2; 21 pw3[0]=1; 22 for (i=1;i<=13;i++) 23 pw3[i]=pw3[i-1]*3; 24 for (i=0;i<pw2[13];i++) 25 { 26 s[i]=1; 27 for (j=0;j<=11;j++) 28 if ((i&pw2[j])&&(i&pw2[j+1])) s[i]=0; 29 } 30 ans=1; 31 for (i=1;i<=n;i++) 32 if (i%2&&i%3) 33 { 34 k=1; 35 q=Log3[n/i]+1; 36 for (j=0;j<pw2[q];j++) 37 f[0][j]=s[j]; 38 while (i*pw2[k]<=n) 39 { 40 p=Log3[n/(i*pw2[k])]+1; 41 for (j=0;j<pw2[p];j++) 42 if (s[j]) 43 { 44 f[k][j]=0; 45 for (l=0;l<pw2[q];l++) 46 if (s[l]&&((j&l)==0)) 47 { 48 f[k][j]=(f[k][j]+f[k-1][l])%Mod; 49 } 50 } 51 else f[k][j]=0; 52 q=p; 53 k++; 54 } 55 sum=0; 56 for (j=0;j<pw2[q];j++) 57 sum=(sum+f[k-1][j])%Mod; 58 ans=(1ll*ans*sum)%Mod; 59 } 60 cout<<ans; 61 }