• [CF1216E] Numerical Sequence


    题目

    (直接上简陋翻译中文版了……)有一个无限长的数字序列,其组成为1 1 2 1 2 3 1 …… 1 2 …… n ……,即重复的1 ~ 1 , 1 ~ 2 …… 1 ~ n,给你一个 (k) ,求第(k)个数字是什么。

    解说

    easy version

    其实这个题是有两个版本的,easy version和hard version,前者(k<=10^9),后者(k<=10^{18})之后呢我们亲爱的姚老师没有任何前置地给我们上了hard version。
    所以看题吧。其实这两个版本思路是相通的,但是有些微的不同。先看easy version。
    (k)“很小”(相较于hard而言),我们可以维护三个数组(abc)(a[i])表示数值为i的数字的长度,例如 (a[1 ] = 1 , a [ 23 ] = 2) (因为(k<=10^9)所以开的下);
    (b[ i ]) 表示 数字 1~i 的总长度 ,例如 (b[ 9 ] = 9 , b[ 11 ] = 13 (1234567891011)) ;
    (c[ i ]) 表示 序列 1~1 、1~2、…… 、1~i-1 、 1~ i ,长度总和,即为题目所描述序列.。
    显然(b)(a)的前缀和,(c)(b)的前缀和。
    输入长度(k),因为前缀和具有单增的性质,可以二分(c)数组,划分出(k)在序列中属于 1~y((y>=k)),即为在数组(b)中的索引(y) (注意(b)数组的定义,(b [ y ]) 表示 数字1~y 的总长度); 然后再二分(b)数组,就能知道,他是属于哪个数字的了(设值数字为(z)),最后通过 (a[ z ]) 枚举位数即可求出答案。(一层层向下找)。

    easy代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=4e4+5;
    //为啥是这个数?a位置能够取得的最大值 < n * (n + 1) / 2 = k ,所以maxn大约是sqrt( k )
    int a[maxn],b[maxn],c[maxn];
    int read(){
        int x=0,t=1;
        char ch=getchar();
        while(!isdigit(ch)){ if(ch=='-')t=-1; ch=getchar(); }
        while(isdigit(ch)){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*t;
    }
    int main(){
        for(int i=1;i<=maxn;i++){///预处理
            int x=i;
            while(x){
                x/=10;
                a[i]++;
            }
            b[i]=b[i-1]+a[i];//b是a的前缀和
            c[i]=c[i-1]+b[i];//c是b的前缀和
        }
        int T=read();
        while(T--){
            int n=read(),pos;
            pos=lower_bound(c+1,c+maxn+1,n)-c;
            n-=c[pos-1];// 减掉多余的 1~pos-1, 1~pos-2 …… 1~1 序列的长度,即定位到 1~pos
            pos=lower_bound(b+1,b+maxn+1,n)-b;
            n-=b[pos-1];// 同上,定位到 数值 pos
            int ans=pos;
            n=a[pos]-n; //因为序列是高位到低位,所以做了一下逆转;
            while(n--) ans/=10; //枚举位数
            printf("%d
    ",ans%10);
        }
        return 0;
    }
    

    hard version

    这里是我们看到的问题……
    思路相差不多,但是(k)太大了(a)开不下,需要想办法解决。
    所以需要对前缀和数组经过些许修改优化。明显的,之前a数组有很多连续项是相等的,因此可以用等差数列求和的思想,(每增加一个数字,在位数相同的固定区间内,长度增加的也是相同的),来重新定义这些数组。具体见代码注释。

    hard代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=10;
    LL a[15],b[15],c[15];
    // ai 表示位数是i的数字总长度;1~9 10~99 ...
    // b是a的前缀和 1~9 1~99 1~999 ...
    // c是序列总长度,等差数列求和
    // c1 1~1+1~2+...+1~9
    // c2 c1+ 1~10+1~11+...+1~99
    // c3 c1+c2+ 1~100+...+1~999
    LL read(){
        LL x=0,t=1;
        char ch=getchar();
        while(!isdigit(ch)){ if(ch=='-')t=-1; ch=getchar(); }
        while(isdigit(ch)){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*t;
    }
    int main(){
        #define int LL
        for(int i=1;i<=N;i++){
            int x=i,l=1,r=0;
            while(x--) l*=10,r=r*10+9;
            l/=10;
            a[i]=(r-l+1)*i;
            b[i]=b[i-1]+a[i];//b 每次加一层的长度
            c[i]=c[i-1]+(b[i-1]+i+b[i])*(r-l+1)/2;// 1~1 -- 1~9 d=1; 1~10,1~11 -- 1~99 d=2; (d是公差)
        }
        int T=read();
        while(T--){
            int n=read();
            int pos=lower_bound(c+1,c+N+1,n)-c;
            n-=c[pos-1];
            int l=1,r=0;
            int x=pos,t=b[pos-1];
            while(x--) l*=10,r=10*r+9;
            l/=10;
            int L=l;
            while(l<=r){
                int mid=(l+r)>>1;
                int cnt=mid-L+1;
                if( (2*t+pos+cnt*pos)*cnt/2>=n) r=mid-1;
                else l=mid+1;
            }
            int cnt=l-L;
            n-=(2*t+pos+cnt*pos)*cnt/2;
            pos=lower_bound(b+1,b+N+1,n)-b;
            n-=b[pos-1];
            int ans=1;
            for(int i=1;i<pos;i++) ans*=10;
            t=(n-1)/pos;
            n-=t*pos;
            ans+=t;
            n=pos-n;
            while(n--) ans/=10;
            printf("%lld
    ",ans%10);
        }
        return 0;
    }
    

    借鉴博客https://www.cnblogs.com/DeepJay/p/12025209.html
    幸甚至哉,歌以咏志。

  • 相关阅读:
    html5学习笔记2——新元素
    html5学习笔记——基础
    html学习笔记之2——多媒体
    Python调试打印错误信息
    Python随机字符串验证码
    js传递数组
    js上传图片并预览
    JS获取当前日期、比较日期大小
    nrm管理npm源
    使用Git Subtree在多个项目中共用同一个子项目
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/12788692.html
Copyright © 2020-2023  润新知