【数学】咕咕东的奇妙序列

在这里插入图片描述
在这里插入图片描述

题解

可以观察出奇妙序列的一些数学规律,如奇妙序列的长度和末尾数 n n n的关系,每一个区间 [ 1 , i ] [1,i] [1,i]中前 j j j个数的长度。故我们分成三步,第一步是求解 k k k所在的 [ 1 , i ] [1,i] [1,i]区间,之后再在这一段区间里求解具体的数 j j j,之后直接取出对应的数字即可。
先求出 [ 1 , i ] [1,i] [1,i]的长度规律,可以发现:
1 < = i < = 9 ,   l e n = i ; 1<=i<=9, \ len=i; 1<=i<=9, len=i;
10 < = i < = 99 ,   l e n = 2 ∗ i − 9 ; 10<=i<=99,\ len=2*i-9; 10<=i<=99, len=2i9;
100 < = i < = 999 ,   l e n = 3 ∗ i − ( 99 + 9 ) ; 100<=i<=999,\ len=3*i-(99+9); 100<=i<=999, len=3i(99+9);
. . . ... ...
1 0 e < = i < = 1 0 e + 1 − 1 ,   l e n = e ∗ i − ( 1 0 e + 1 − 1 + . . . + 99 + 9 ) ; 10^e<=i<=10^{e+1}-1,\ len=e*i-(10^{e+1}-1+...+99+9); 10e<=i<=10e+11, len=ei(10e+11+...+99+9);
其中 e e e代表数字 i i i长度。
如此我们可以根据 i i i求出这个区间的长度以及末尾数字为n的奇妙序列总长 ∑ i = 1 n l e n i {\displaystyle \sum _{i=1}^{n}len_{i}} i=1nleni,复杂度是 O ( e ) , e < = 18 O(e),e<=18 O(e),e<=18
那么如何由 k k k推导出对应的 i i i呢,由于这个规律中, i , i − 1 i,i-1 i,i1之间没有明显的权重差异,不能用取模分离出 i i i。而求解序列长度的复杂度几近于一个大常数,故可以采用设解验证的二分。找出对应的 i i i,再二分找出 j j j,具体求法和公式实现见下列程序。

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
using namespace std;
#define rep(i,s,t) for(long long i=s;i<=t;i++)
#define mem(a,s) memset(a,s,sizeof(a))
typedef long long ll;
ll sum(ll k){
    ll K = k, len = 0;
    while(K){
        K /= 10;
        len++;
    }
            //从1开始
    ll tt = 0,e=0,x=0,s=1;
    rep(i,1,len){
        e =e * 10 + 9;
        if(i==len)
            e = k;   
        tt += (i * (e + s) * (e - s + 1) / 2 - (e - s + 1) * x);
        s *= 10;x += e;
    }
    return tt;
}
ll erfen(ll k){
    //<=的第一个位置
    ll l=1,r=1e9,ans=0;
    while(l<=r){
        ll m=(l+r)>>1;
        if(sum(m)>=k){
            ans=m;
            r=m-1;
        }else l=m+1;
    }
    return ans;
}
ll tt(ll x){
    ll K = x,tx = 0,len = 0,ttx=0;
    while(K){
        K /= 10;
        len++;
        if(K){
            tx = tx * 10 + 9;
            ttx += tx;
        }
            
    }
    return len * x - ttx;
}
int make(ll k,ll R){
    ll l=0,r=R,ans=0;
    while(l<=r){
        ll m=(l+r)>>1;
        if(tt(m)>=k){
            ans=m;
            r=m-1;
        }else l=m+1;
    }
    k -= tt(ans - 1);
    string s = to_string(ans);
    return s[k - 1]-'0';
    
}
int main(){
 // freopen("in.txt","r",stdin);
    cin.sync_with_stdio(false);
    cout << sum(1e9) << endl;
    int Q;
    cin >> Q;
    while(Q--){
        long long k;
        cin >> k;
        long long f = erfen(k);
        cout << make(k-sum(f-1),f)<<endl;
    }
     return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值