zoukankan      html  css  js  c++  java
  • 【bzoj2734】集合选数(有点思维的状压dp)

      题目传送门:bzoj2734

      这题一个月前看的时候没什么头绪。现在一看,其实超简单。

      我们对于每个在$ [1,n] $范围内的,没有因数2和3的数$ d $,将它的倍数$ 2^a 3^b d $一起处理。因为每个数$ d $之间没有2和3作为公因数,所以统计时互不影响。

      对于$ d $的倍数$ 2^a 3^b d $,我们可以发现如果把它按因子2的次数为行,因子3的次数为列,把这些数排列在一个矩形中,相当于是在一个阶梯状的棋盘上选择最多的互不相邻的格子。这个可以用状压dp计算。

      其实这题的主要难度在于复杂度的分析,我一个月前也是没算出复杂度然后主观否决了这个方案。

      于是我们现在来分析一下时间复杂度:

        对于数$ d $,将其倍数$ 2^a 3^b $排列成的矩形的规模是$ log_2(frac{n}{d}) imes log_3(frac{n}{d}) $的,而对于一个$ n imes m $的矩形进行状压dp选择最多的互补相邻的格子的时间复杂度为$ O(2.618^mn) $(因为可以预处理出每一行的所有满足选择的格子互不相邻的有效状态,而有效状态的数量是$ O(1.618^m) $的,所以综合起来复杂度就是$ O(2.618^mn) $)。因此,处理数d时所花费的时间复杂度为$ O(frac{n}{d} log(frac{n}{d})) $。

        因此,总时间复杂度为:$ sum_{d=1}^{n}frac{n}{d} log(frac{n}{d}) = n log^2 n $

      代码:

    #include<cstdio>
    #include<cmath>
    #define ll long long
    #define mod 1000000001
    #define maxn 100010
    int vis[maxn],can[20][1<<15],st[310];
    ll a[20][20],f[20][310];
    int n;
    int work(int x)
    {
        int w=(int)(log(n/x)/log(3)+1e-10)+1,h=(int)(log(n/x)/log(2)+1e-10)+1,tot=0;
        a[1][1]=x;
        for(int i=2;i<=w;i++)
            a[1][i]=a[1][i-1]*3;
        for(int i=2;i<=h;i++)
            for(int j=1;j<=w;j++)
                a[i][j]=a[i-1][j]*2;
        for(int i=1;i<=h;i++)
            for(int j=1;j<=w;j++)
                if(a[i][j]<=n)vis[a[i][j]]=1;
        for(int i=0;i<=h;i++)
            for(int j=0;j<1<<w;j++){
                int flag=1;
                for(int k=0;k<w;k++)
                    if((j&(1<<k))&&a[i][k+1]>n){
                        flag=0; break;
                    }
                if(flag)can[i][j]=1;
                else can[i][j]=0;
            }
        for(int i=0;i<1<<w;i++)
            if(!(i&(i<<1))&&!(i&(i>>1)))st[++tot]=i;
        f[0][1]=1;
        for(int i=1;i<=h;i++)
            for(int j=1;j<=tot;j++){
                f[i][j]=0;
                for(int k=1;k<=tot;k++)
                    if(can[i][st[j]]&&can[i-1][st[k]]&&!(st[j]&st[k])){
                        f[i][j]+=f[i-1][k];
                        if(f[i][j]>=mod)f[i][j]-=mod;
                    }
            }
        int ans=0;
        for(int i=1;i<=tot;i++)
            if(can[h][st[i]]){
                ans+=f[h][i];
                if(ans>=mod)ans-=mod;
            }
        return ans;
    }
    int main()
    {
        scanf("%d",&n);
        ll ans=1;
        for(int i=1;i<=n;i++)
            if(!vis[i])ans=ans*work(i)%mod;
        printf("%lld
    ",ans);
    }
    bzoj2734
  • 相关阅读:
    phpstorm 使用 Xdebug 调试代码
    frp 实现内网穿透(Windows 版)
    update-alternatives 使用详解
    Linux 虚拟机使用 xshell 连接 (debian、kali、CentOS)
    PHP xml 转数组 数组转 xml 操作
    Mac上查看当前安卓手机上打开的app的包名和主程序入口
    启动appium server时打印日志时间
    App自动化测试框架学习探索--从零开始设计
    System.getProperty("user.dir")获取的到底是什么路径?
    读取Excel文件,抛出类似Cleaning up unclosed ZipFile for archive D:projectmyTestautoAppUIexcelMode用例模板2.xlsx 错误解决
  • 原文地址:https://www.cnblogs.com/quzhizhou/p/10124846.html
Copyright © 2011-2022 走看看