• 【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
  • 相关阅读:
    Reactive(1) 从响应式编程到"好莱坞"
    [动图演示]Redis 持久化 RDB/AOF 详解与实践
    补习系列(22)-全面解读 Spring Profile 的用法
    Android手机打造你的Python&Java开发工具!
    人工神经网络模型种类
    最小二乘拟合
    LDA主体模型
    Logistic Regression求解classification问题
    batch gradient descent(批量梯度下降) 和 stochastic gradient descent(随机梯度下降)
    SVM实验
  • 原文地址:https://www.cnblogs.com/quzhizhou/p/10124846.html
Copyright © 2020-2023  润新知