zoukankan      html  css  js  c++  java
  • 【题解】数 [Bzoj5282]

    【题解】数 [Bzoj5282]

    传送门:( ext{[Bzoj5282]})

    【题目描述】

    众所周知,萌萌哒六花不擅长数学,所以勇太给了她一些数学问题做练习,其中有一道是这样的:

    一个正整数被称为是好的当且仅当它的十进制表示是其二进制表示的后缀。

    例如正整数 ((100)_{10}) 就是一个好的正整数,它的二进制表示是 ((1100100)_{2}),而正整数 ((2)_{10})

    就不是一个好的正整数,它的二进制表示是 ((10)_{2})

    现在勇太想要知道第 (K) 小的好的正整数。

    当然,这个问题对于萌萌哒六花来说实在是太难了,你可以帮帮她吗?

    【分析】

    又是一道 ( ext{jiry_2}) 搞出来报复社会的毒瘤题。。。

    萌萌哒六花好评。

    萌萌哒りっか

    考试时瞎 (jb) 枚举搞到了 (30pts),然后就不会了。

    考虑在一个 (k) 位十进制数 ((x)_{10}) 的最前面加一个 (1) (即第 (k+1) 位)所产生的变化,对于数值大小来说增加了 (10^k),而这东西一定是 (2^k) 的倍数,所以在不考虑进位的情况下 ((x)_{2}) 的第 (k+1) 位数值会增加 (5^k),考虑进位后可以发现对 ((x)_{2}) 的后 (k) 位始终没有影响,于是暴力做法就出来了:

    从小到大枚举 (k),用一个集合 (Q) 维护当前所有的合法数字,每次 (k) 增大后尝试在这些数的前面加 (1/0)(加 (0) 就是不变),并将合法的加入新集合,不合法的舍去。当求出第 (K) 个合法数字时就直接输出。

    发现这玩意儿要写高精,但复杂度双双爆炸,按照吉司机的神仙思路,把大数压成 ((bit=2^{base})) 进制(标程里面 (base=25)),复杂度直接降到 (O(frac{KL}{base}))(L) 为数字长度)。

    说实话这个复杂度的分析我是真没看懂,总之能过就行了...

    【Code】

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=5e5+3;
    const int bit=(1<<25),base=25;//bit:高精度大数的进制
    struct Int{
        int l,a[150];
        Int(Re O=0){memset(a,0,sizeof(a)),a[l=1]=O;}
        inline void operator=(const int O){*this=Int(O);}
        inline void operator+=(const Int &O){//高精加高精
            l=max(l,O.l);
            for(Re i=1;i<=l;++i)a[i]+=O.a[i];l+=2;
            for(Re i=1;i<l;++i)if(a[i]>=bit)
                a[i+1]+=(a[i]>>base),a[i]&=(bit-1);//a[i+1]+=a[i]/bit,a[i]%=bit;
            while(l>1&&!a[l])--l;//去掉前导0
        }
        inline void operator*=(const int O){//高精乘低精
            for(Re i=1;i<=l;++i)a[i]*=O;l+=2;
            for(Re i=1;i<l;++i)if(a[i]>=bit)
                a[i+1]+=(a[i]>>base),a[i]&=(bit-1);//a[i+1]+=a[i]/bit,a[i]%=bit;
            while(l>1&&!a[l])--l;//去掉前导0
        }
        inline void print(){//转为十进制数输出
            vector<int>ans;
            while(l&&a[l]){
                Re yu=0;
                for(Re i=l,x;i>=1;--i)//高精除低精,即a/=10
                    x=a[i],a[i]=((yu<<base)+x)/10,yu=((yu<<base)+x)%10;//即a[i]=(yu*bit+x)/10,yu=(yu*bit+x)%10
                ans.push_back(yu);//算出来的余数(a%10)就是十进制下的当前位的值
                while(l>1&&!a[l])--l;//去掉前导0
            }
            for(Re i=ans.size()-1;i>=0;--i)printf("%d",ans[i]);//获取值是从低位到高位,所以要倒叙输出
        }
        inline int judge(Re j){return a[j/base+1]&(1<<j%base);}//判断二进制下第j位的数值是否为1
    }B,Q[N];
    int K,O,fa[N];
    inline int find(Re x){return x==fa[x]?x:fa[x]=find(fa[x]);}
    int main(){
    //  freopen("number.in","r",stdin);
    //  freopen("number.out","w",stdout);
        scanf("%d",&K),Q[fa[1]=O=1]=0,B=1;//先初始化一个0
        for(Re k=0;;++k){
            Re ed=find(O),pre=0;
            for(Re i=find(1);pre!=ed;pre=i,i=find(i+1)){
                Int tmp=Q[i];tmp+=B;//B: 10^k
                if(tmp.judge(k)){//十进制下第k位加1的情况,如果加上1*10^k之后二进制第k位为1则可以加入该点
                    Q[++O]=tmp,fa[O]=O;
                    if(!--K){tmp.print();return 0;}
                }
                if(Q[i].judge(k))fa[i]=find(i+1);//十进制下第k位加0的情况,如果二进制第k位为1则需要删掉该点
            }
            B*=10;
        }
        fclose(stdin);
        fclose(stdout);
        return 0; 
    }
    
  • 相关阅读:
    dpdk学习笔记2
    <c和指针>学习笔记2之数据和语句
    dpdk数据包捕获技术笔记1
    <c++primer plus>学习笔记1之第八章函数探幽
    准备面试的那些事儿1
    tcp_fast_open的概念 作用以及实现(转)
    tcp/ip详解(转)
    Build step 'Execute Windows batch command' marked build as failure
    vmware12如何安装macOSX虚拟机
    如何将exe注册为windows服务,直接从后台运行
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12699839.html
Copyright © 2011-2022 走看看