zoukankan      html  css  js  c++  java
  • BZOJ_2734_[HNOI2012]集合选数_构造+状压DP

    BZOJ_2734_[HNOI2012]集合选数_构造+状压DP

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

    分析:

    我们构造出一个矩阵

    $egin{matrix}
        1&2^03^1&2^03^2\
        2^13^0&2^13^1&2^13^2\
        2^23^0&2^23^1&2^23^2\
    end{matrix}
    $

    发现矩阵的相邻两个格子的数不能同时取

    状压DP一下

    要把所有不在矩阵中的数当作1重新构造,比如5,7等等

    每个矩阵的结果乘起来就是答案

    代码:

    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    #define LL long long
    LL p=1000000001,A,f[18][1<<12];
    int vis[100050],s[18],mat[18][18];
    LL ans=1;
    void build(int x){
        int n=1,m=1,now=x;
        while(now*3<=A)m++,now*=3;
        now=x;
        while(now*2<=A)n++,now<<=1;
        int mask=(1<<m)-1;
        memset(s,0,sizeof(s));
        memset(f,0,sizeof(f));
        memset(mat,0,sizeof(mat));
        mat[1][1]=x;vis[x]=1;
        for(int i=2;i<=m;i++){
            mat[1][i]=mat[1][i-1]*3;
            vis[mat[1][i]]=1;
        }
        s[1]=mask;
        for(int i=2;i<=n;i++){
            mat[i][1]=mat[i-1][1]*2;
            vis[mat[i][1]]=1;
            for(int j=2;j<=m;j++){
                mat[i][j]=mat[i-1][j]*2;
                if(mat[i][j]>A){
                    s[i]=mask^((1<<m-j+1)-1);
                    break;
                }
                vis[mat[i][j]]=1;
            }
            if(!s[i])s[i]=mask;
        }
        f[0][0]=1;
        s[0]=mask;
        for(int i=0;i<n;i++){
            for(int j=0;j<=mask;j++){
                if((j|s[i])!=s[i])continue;
                if(j&(j<<1))continue;
                for(int k=0;k<=mask;k++){
                    if((k|s[i+1])!=s[i+1])continue;
                    if(k&(k<<1))continue;
                    if(j&k)continue;
                    f[i+1][k]+=f[i][j];
                    f[i+1][k]%=p;
                }
            }
        }
        LL re=0;
        for(int i=0;i<=mask;i++)re+=f[n][i],re%=p;
        ans=re*ans%p;
     
        /*for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                printf("%d ",mat[i][j]);
            }
            puts("");
        }*/
     
        /*for(int i=1;i<=n;i++){
            printf("%d
    ",s[i]);
        }*/
    }
    int main(){
        scanf("%lld",&A);   
        for(int i=1;i<=A;i++){
            if(!vis[i])build(i);
        }
        printf("%lld",ans);
    }
    
  • 相关阅读:
    让电脑中的文件后缀显示完整
    [trie][异或] hdu 6625 three arrays
    [全排列] hdu 6628 permutation 1
    [模板][最大流]dinic
    [模板]矩阵十进制快速幂
    CCPC-Wannafly Summer Camp 2019 Day1
    [技巧]ARubbish
    [dp]第十届蓝桥杯国赛CB组C
    [暴力]分块
    [模板]主席树及其应用
  • 原文地址:https://www.cnblogs.com/suika/p/8506730.html
Copyright © 2011-2022 走看看