• BZOJ 2734: [HNOI2012]集合选数 [DP 状压 转化]


    传送门

    题意:对于任意一个正整数 n≤100000,如何求出{1, 2,..., n} 的满足若 x 在该子集中,则 2x 和 3x 不能在该子集中的子集的个数(只需输出对 1,000,000,001 取模的结果)


    好巧妙的转化啊:

    构造一个矩阵,把限制关系转化成矩阵的相邻元素不能同时选

    1 3  9  27…

    2 6 18 54…

    4 12 36 108…

    然后愉♂悦的状压DP就可以啦

    注意每一个既不被$2$又不被$3$整除的数都可以作为矩阵的第一个元素,还有矩阵不一定填满

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int N=20,S=(1<<11)+5,P=1e9+1;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    int n,f[N][S];
    inline void mod(int &x){if(x>=P) x-=P;}
    ll ans=1;
    int col[N];
    void dp(int x){
        for(int i=x,r=1;i<=n;i*=2,r++){
            int c=0;
            for(int j=i;j<=n;j*=3,c++);
            col[r]=1<<c;
            //printf("col %d %d
    ",r,c);
        }
    
        int r=0;
        for(int i=x;i<=n;i*=2,r++);
        //printf("dp %d %d 
    ",x,r);
    
        f[0][0]=1;col[0]=1;
        for(int i=1;i<=r;i++)
            for(int j=0;j<col[i];j++) if( (j&(j<<1))==0 ){
                f[i][j]=0;
                for(int k=0;k<col[i-1];k++) if( (j&k)==0 ) mod(f[i][j]+=f[i-1][k]);
                //printf("f %d %d %d
    ",i,j,f[i][j]);
            }
        int _=0;
        for(int j=0;j<col[r];j++) mod(_+=f[r][j]);
        //printf("_ %d
    ",_);
        ans=ans*_%P;
    }
    int main(){
        freopen("in","r",stdin);
        n=read();
        for(int i=1;i<=n;i++) if(i%3 && i%2) dp(i);
        printf("%lld",ans);
    }
  • 相关阅读:
    [学习总结]1、View的scrollTo 和 scrollBy 方法使用说明和区别
    [项目总结]Android 手动显示和隐藏软键盘
    Ubuntu系统下C语言编程
    windows API程序设计(一个简单的窗口)
    小程序的四种文件类型和基本结构
    小乌龟使用错误
    ROS通信编程与仿真工具
    小程序的事件机制--捕捉与回调,catch与bind
    Sql 中Collate用法
    tf.data.dataset
  • 原文地址:https://www.cnblogs.com/candy99/p/6512970.html
Copyright © 2020-2023  润新知