集合選數
阿新 • • 發佈:2018-12-15
集合選數
題目描述
《集合論與圖論》這門課程有一道作業題,要求同學們求出{1, 2, 3, 4, 5}的所有滿足以 下條件的子集:若x 在該子集中,則2x 和3x 不能在該子集中。同學們不喜歡這種具有列舉性 質的題目,於是把它變成了以下問題:對於任意一個正整數n≤100000,如何求出{1, 2,..., n} 的滿足上述約束條件的子集的個數(只需輸出對1,000,000,001 取模的結果),現在這個問題就 交給你了。
輸入
輸入檔案input.txt 只有一行,其中有一個正整數n,30%的資料滿足n≤20。
輸出
輸出檔案output.txt 僅包含一個正整數,表示{1, 2,..., n}有多少個滿足上述約束條件 的子集。
樣例輸入
4
樣例輸出
8
提示
【樣例解釋】
有8 個集合滿足要求,分別是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。
solution
神題,想不到
如果當前一個數為x,把x*2^p*3^q 都抓出來,當成一個集合
1x | 3x | 9x | 27x | 81x |
2x | 6x | 18x | . | . |
4x | 12x | . | . | . |
8x | . | . | . | . |
16x | . | . | . | . |
類似這樣構出一個矩陣
不同的矩陣之間互不影響
那麼我們肯定是要在矩陣中選若干個數,使得任意兩列都不相鄰
注意到列數少只有11 ,可以狀壓dp解決
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define maxn 100005 #define ll long long #define mod 1000000001 using namespace std; int n,flag[maxn],Max[22]; ll ans=0,a[20][20],f[20][maxn]; ll work(int k){ int M; for(int i=0;i<19;i++){ int t=(k*(1<<i)); if(t>n){M=i;break;} for(int j=0,now=1;j<19;j++,now*=3){ a[i][j]=t*now; //cout<<i<<' '<<j<<' '<<a[i][j]<<endl; if(a[i][j]>n)break; } } for(int i=0;i<M;i++){ Max[i]=0; for(int j=0;j<19;j++){ if(a[i][j]>n)break; if(!flag[a[i][j]])flag[a[i][j]]=1; Max[i]+=(1<<j); } } for(int i=0;i<=M;i++) for(int j=0;j<=Max[i];j++)f[i][j]=0; for(int j=0;j<=Max[0];j++)if(!(j&(j<<1)))f[0][j]=1; for(int l=0;l<M;l++){ for(int i=0;i<=Max[l];i++){ for(int j=0;j<=Max[l+1];j++){ if((!(i&j))&&!(j&(j<<1))){ f[l+1][j]=(f[l+1][j]+f[l][i])%mod; } } } } return f[M][0]; } int main() { cin>>n; ans=1; for(int i=1;i<=n;i++)if(!flag[i])ans=(ans*work(i))%mod; cout<<ans<<endl;