题目描述
《集合论与图论》这门课程有一道作业题,要求同学们求出{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:
4
输出样例#1:
8 【样例解释】 有8 个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。
发现可以将1-n分成若干组,其中每一组相互独立,第i组可以表示成形如
x 2x 4x 8x
3x 6x 12x 24x
9x 18x 36x 72x
这样随着行数递增列数单调不增的少一块的矩阵。
满足条件的方案就是矩阵中不能选出相邻的数。
因为每一组相互独立,所以可以将每一组的答案乘起来,得到最后的答案。
所以接下来看一下如何求一组的答案。
发现行数最多是10(3^11>10^5),所以可以把一列作为状态进行转移,
需要注意的是随着枚举的列编号增大,一列的行数是可能会减少的,这时候需要判定一下。
细节比较多(我的代码是把*3横着放所以一行一个状态),但是我的代码还算比较快的(bzoj rank 40/9??)。
/************************************************************** Problem: 2734 User: JYYHH Language: C++ Result: Accepted Time:28 ms Memory:1408 kb ****************************************************************/ #include<bits/stdc++.h> #define ll long long #define maxn 100005 #define ha 1000000001 using namespace std; int ci[20],n,m; int zt[1005],num=0; bool v[maxn]; int f[20][250]; ll ans=1; inline void init(){ ci[0]=1; for(int i=1;i<=15;i++) ci[i]=ci[i-1]+ci[i-1]; } inline ll solve(int pos){ ll an=0; int l=0,h=0; for(int i=pos;i<=n;i<<=1) for(int j=i;j<=n;j*=3) v[j]=1; for(int i=pos;i<=n;i*=3,l++); num=0,f[0][1]=1; for(int i=0;i<ci[l];i++) if(!(i&(i<<1))) zt[++num]=i; h=1; for(;pos<=n;pos<<=1,h++){ l=0; for(int j=pos;j<=n;j*=3,l++); for(int j=1;j<=num&&zt[j]<ci[l];j++) for(int k=1;k<=num;k++) if(!(zt[j]&zt[k])){ f[h][j]+=f[h-1][k]; if(f[h][j]>=ha) f[h][j]-=ha; } } h--; for(int j=1;j<=num;j++){ an+=f[h][j]; if(an>=ha) an-=ha; } for(int i=1;i<=h;i++) for(int j=1;j<=num;j++) f[i][j]=0; return an; } int main(){ init(); scanf("%d",&n); for(int i=1;i<=n;i++) if(!v[i]){ ans=ans*solve(i); if(ans>=ha) ans-=ans/ha*ha; } printf("%lld\n",ans); return 0; }