CF1216E Numerical Sequence

题目链接

问题分析

奇奇怪怪的题。。。

首先思路达成一致,从大到小一步一步确定位置。

我们一边分析,一边讲算法。

112123123412345123456123456712345678123456789123456789101234567891011123456789101112

假设我们现在要找的是这个串中的倒数第二个位置(就是1),我们可以这样做:

首先,我们想象着把串分开,变成

1
12
123
1234
12345
123456
1234567
12345678
123456789
12345678910
1234567891011
123456789101112

由于我们发现, 最后一个数字 位数相同 的串 的长度 是一个等差数列,可以方便地求得数列和。所以我们可以枚举 所求位置所在串 的 最后一个数字 的 位数。

在这个例子中,我们先枚举 最后一个数字 的 位数 为\(1\),可以算得这样的串的总长是 \((1+9)*9/2\)。发现不到所求位置,于是把这些串扔掉,顺便所求位置减\(45\)

12345678910
1234567891011
123456789101112

然后枚举 最后一个数字 的 位数 为\(2\),可以算得这样串的总长是 \((11+(9+2*90))*90/2\)大于所求位置。

所以我们知道了 所求位置 所在串 的 最后一个数 的 位数 是 \(2\)

然后同样的,根据这个等差数列,我们可以二分求得 所求位置 所在串 的 最后一个数 是多少。

这个例子中,最后锁定在串

123456789101112

然后……

我们可以重复上面的操作,把串分成

1 2 3 4 5 6 7 8 9 10 11 12

然后重复上面的第一步操作确定 所求位置 所在数 的 位数。只不过这次长度数列不再是等差数列,而是值等于数字位数的常数列。

在这个例子中,我们删掉了长度为\(1\)的,留下

10 11 12

然后可以直接算出 所求位置在 12 里。然后问题就解决了!

参考程序

#include <bits/stdc++.h>
#define LL long long
using namespace std;

void Work() {
    LL n;
    scanf( "%lld", &n );
    LL LastLen = 0, Len, Count;
    for( Len = 1; ; ++Len ) {
        Count = 9;
        for( LL i = 1; i < Len; ++i ) 
            Count = Count * 10;
        LL Sum = ( LastLen + Len + LastLen + Count * Len ) * Count / 2;
        if( n <= Sum ) break;
        n -= Sum;
        LastLen += Count * Len;
    }
    LL Left = 1, Right = Count, Mid, Ans;
    while( Left <= Right ) {
        Mid = ( Left + Right ) >> 1;
        LL Sum = ( LastLen + Len + LastLen + Mid * Len ) * Mid / 2;
        if( Sum >= n ) {
            Ans = Mid;
            Right = Mid - 1;
        } else Left = Mid + 1;
    }
    --Ans;
    n -= ( LastLen + Len + LastLen + Ans * Len ) * Ans / 2;
    ++Ans;
    for( Len = 1; ; ++Len ) {
        Count = 9;
        for( LL i = 1; i < Len; ++i ) 
            Count = Count * 10;
        LL Sum = Count * Len;
        if( Sum >= n ) break;
        n -= Sum;
    }
    LL Num = ( n + Len - 1 ) / Len;
    n = n - ( Num - 1 ) * Len;
    LL T = 1;
    for( LL i = 1; i < Len; ++i ) T = T * 10;
    Num = T + Num - 1;
    T = Len - n + 1;
    for( LL i = 1; i < T; ++i ) Num = Num / 10;
    printf( "%lld\n", Num % 10 );
    return;
}

int main() {
    LL Query;
    scanf( "%lld", &Query );
    for( LL i = 1; i <= Query; ++i ) Work();
    return 0;
}

转载于:https://www.cnblogs.com/chy-2003/p/11574446.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值