1. 程式人生 > >[HNOI2012]集合選數

[HNOI2012]集合選數

mat sample algorithm ios 輸入輸出 hnoi 輸入輸出格式 style reg

題目描述

《集合論與圖論》這門課程有一道作業題,要求同學們求出{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}。
對於一個數,我們可以把它分解成$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 }

[HNOI2012]集合選數