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

BZOJ2734 [HNOI2012] 集合選數

沒有 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] 集合選數