Codeforces 1216E Numerical Sequence 二分

题目链接:http://codeforces.com/contest/1216/problem/E2

题意:有一个无限长的数字序列,其组成为1 1 2 1 2 3 1.......1 2 ... n...,即重复的1~1,1~2....1~n,给你一个k,求第k(1e18)个数字是什么

分析:首先,我们先来大致算一下大概有多少个块(一个块值一次1到i)我们假设第i块的长度为i(事实上要更大,因为到了后面,有两位数,三位数...),这样算出来,前n块的和要达到1e18的话,n大致为1e9(等差数列求和是平方的复杂度),我们就必须找到一个较快的速度找到它属于第几块

因为前i+1块的和肯定是大于前i块的和的,也就是说单增,我们可以用二分来处理这个问题,具体看代码

几个难懂的地方:

add值的是比当前长度短的数的和,比如当长度为3时,所有长度为2的数的长度和为189(1-9,10-99)

cnt*(cnt+1)/2*len是因为我们我们求的是1个当前长度,2个当前长度的数一直到cnt个当前长度的数,也就是等差数列求和,然后再乘个len就好

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+7;
const int N=1e3+7;
const int inf=0x3f3f3f3f;
ll k;
ll get(ll x){
    return x*(x+1)/2;
}
ll sumto(int x,int need){
    ll sum=0;
    ll add=0;
    ll p=1;
    for(int len=1;;len++){
        if(p*10-1<x)
        {
            ll cnt=p*10-p;//长度为Len的数字的个数
            if(need){
                //get(cnt)*len是长度为len的数字的总长度,但因为每个数字都是从1开始的,前面还有长度为1,2..len-1的总和,这个就是add保存的
                //一共有cnt*add个需要加 
                sum+=cnt*add+get(cnt)*len;
                add+=cnt*len;//每次add存的都是比当前长度小的所有数字的长度和 
            }
            else sum+=cnt*len; 
        }
        else
        {
            ll cnt=x-p+1;
            if(need){
                sum+=cnt*add+get(cnt)*len;
            }
            else sum+=cnt*len; 
            break;
        }
        p*=10;
    }
    return sum;
}
int main(){
    int q;scanf("%d",&q);
    while(q--){
        scanf("%I64d",&k);
        int l=1,r=1e9;
        int res=-1;//求的是第一个和比k大的模块 
        while(l<=r){
            int mid=l+r>>1;
            if(sumto(mid,1)>=k){
                res=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        //cout<<res<<endl;
        //cout<<sumto(res-1,1)<<endl;
        k-=sumto(res-1,1);
        l=1,r=res;
        int ans=-1;
        while(l<=r){
            int mid=l+r>>1;
            if(sumto(mid,0)>=k){
                ans=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        //cout<<ans<<" "<<k<<endl;
        k-=sumto(ans-1,0);
        //cout<<k<<endl;
        cout<<to_string(ans)[k-1]<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/qingjiuling/p/11574054.html