bzoj 2734 [HNOI2012]集合选数

http://www.elijahqi.win/archives/3540
Description
《集合论与图论》这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,…, n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就 交给你了。

Input
只有一行,其中有一个正整数 n,30%的数据满足 n≤20。

Output
仅包含一个正整数,表示{1, 2,…, n}有多少个满足上述约束条件 的子集。

Sample Input

4
Sample Output
8

【样例解释】

有8 个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。

HINT

Source
day2

我们可以构造形如以下的一个矩阵
x 3x 9x 27x…
2x 6x 18x 54x
4x 12x 36x 108x
8x 24x 72x 216x
就是这种形式
考虑只要求一下这个矩阵内 互不相邻的取数个数即可
那么这个矩阵构造出来的一定是一开始x 这个数的2^i*3^j这么多次的一个倍数
肯定还有其他数没有出现过 比如x=1的时候5就没有出现过 那么就把x=5 然后再做一遍
这样首先一个矩阵内保证了所有这样的倍数都被取遍了 所以我们再做到不同的数字的时候就可以保证矩阵内不出现相同的数字
那么因为各个矩阵之间是互相独立的 所以乘法原理直接搞即可

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=1e5+10;
const int mod=1000000001;
bool mark[N];int n,a[22][22],dp[22][3000],b[22];
inline void inc(int &x,int v){x=x+v>=mod?x+v-mod:x+v;}
inline int gao(int x){
    a[1][1]=x;memset(dp,0,sizeof(dp));
    for (int i=2;i<=17;++i) {
        a[i][1]=a[i-1][1]*2;if(a[i][1]>n) a[i][1]=n+1;
    }
    for (int i=1;i<=17;++i)
        for (int j=2;j<=11;++j) {
            a[i][j]=a[i][j-1]*3;
            if (a[i][j]>n) a[i][j]=n+1;
        }
    memset(b,0,sizeof(b));
    for (int i=1;i<=17;++i){
        for (int j=1;j<=11;++j){
            if (a[i][j]>n) continue;
            mark[a[i][j]]=1;b[i]|=1<<j-1;
        }
    }
    dp[0][0]=1;
    for (int i=0;i<=17;++i){
        for (int j=0;j<=b[i];++j){if (!dp[i][j]) continue;
            for (int k=0;k<=b[i+1];++k){
                if (j&k) continue;if (k&(k>>1)) continue;
                inc(dp[i+1][k],dp[i][j]);
            }
        }
    }return dp[18][0];
}
int main(){
    freopen("bzoj2734.in","r",stdin);
    n=read();ll ans=1;
    for (int i=1;i<=n;++i) if (!mark[i]) 
    ans=ans*gao(i)%mod;
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/elijahqi/article/details/80493226