BZOJ2734 [HNOI2012] 集合選數
阿新 • • 發佈:2018-11-22
沒有 stack while 復雜度 type ref std 深度 +=
題意
《集合論與圖論》這門課程有一道作業題,要求同學們求出{1, 2, 3, 4, 5}的所有滿足以 下條件的子集:若 x 在該子集中,則 2x 和 3x 不能在該子集中。
同學們不喜歡這種具有枚舉性 質的題目,於是把它變成了以下問題:對於任意一個正整數 n<=100000,如何求出{1, 2,..., n} 的滿足上述約束條件的子集的個數(只需輸出對 1,000,000,001 取模的結果),現在這個問題就 交給你了。
分析
參照hzwer的題解。
寫出如下矩陣
1 3 9 27…
2 6 18 54…
4 12 36 108…
發現最多有11列。。。
我們在其中選取一些數,相鄰的不能選擇
然後就可以狀壓求方案數了,但是5沒有出現,同樣5的倍數也沒有出現,7也如此。。
應該記錄哪些數字出現過,沒出現過就作為矩陣的第一個元素,最後把若幹個矩陣的方案相乘
時間復雜度
因為這是按照整除關系構建的三角形圖,所以三角形並不高,最壞的情況是第一行是1,最後第k行是\(2^k\),深度是\(O(\log n)\)的,每一行的元素最多也是\(O(\log n)\)的,一個元素推到下一行的狀態是\(O(2^k)\)的,越深枚舉次數越多,但點數也越少,可以考慮對每個三角形圖進行分析,一個很難達到的上界時間復雜度是\(O(n \log n)\)。
這大概是口胡,不過還是蠻有道理的、
代碼
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<set> #include<map> #include<queue> #include<stack> #include<algorithm> #include<cstring> #define rg register #define il inline #define co const template<class T>T read() { T data=0; int w=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') w=-1; ch=getchar(); } while(isdigit(ch)) { data=data*10+ch-'0'; ch=getchar(); } return data*w; } template<class T>T read(T&x) { return x=read<T>(); } using namespace std; typedef long long ll; co int MAXN=1e5+7,MAXL=20,mod=1e9+1; int n; int a[MAXL][MAXL]; int b[MAXL],f[MAXL][1<<MAXL]; bool mark[MAXN]; int ans=1; int add(int x,int y) { x+=y; return x>=mod?x-mod:x; } int mul(int x,int y) { return (ll)x*y%mod; } int cal(int x) { memset(b,0,sizeof b); a[1][1]=x; for(int i=2;i<=18;++i) if(a[i-1][1]*2<=n) a[i][1]=a[i-1][1]*2; else a[i][1]=n+1; for(int i=1;i<=18;++i) for(int j=2;j<=11;++j) if(a[i][j-1]*3<=n) a[i][j]=a[i][j-1]*3; else a[i][j]=n+1; for(int i=1;i<=18;++i) for(int j=1;j<=11;++j) if(a[i][j]<=n) { b[i]+=(1<<(j-1)); mark[a[i][j]]=1; } for(int i=0;i<=18;++i) for(int x=0;x<=b[i];++x) f[i][x]=0; f[0][0]=1; for(int i=0;i<18;++i) for(int x=0;x<=b[i];++x) if(f[i][x]) for(int y=0;y<=b[i+1];++y) if((x&y)==0&&(y&(y>>1))==0) f[i+1][y]=add(f[i+1][y],f[i][x]); return f[18][0]; } int main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); read(n); for(int i=1;i<=n;++i) if(!mark[i]) ans=mul(ans,cal(i)); printf("%d\n",ans); return 0; }
BZOJ2734 [HNOI2012] 集合選數