Codeforces Round #587 (Div. 3)E2. Numerical Sequence (hard version)(二分)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43054397/article/details/101201781

题目:E2. Numerical Sequence (hard version)

题意:

一串数11212312341234512345612345671234567812345678912345678910……
有q ( q < 500 )次询问,每个询问一个k ( k <= 1e18) ,求该串数第 k 位是什么数字?

解题:

  1. 求的是第k位数而不是第k个数,9之后数不止一位;
  2. 这里我们用 sum(x) 表示第 x 块的位数和( 从1~x的位数和), ssum(x) 表示x第一次出现时的位数和:
    - 例如 112123 sum(i) 存储 “123” 的位数和 3;
    - 而 ssum(x) 表示 第一次出现3(即"112123")的位数和 6;
  3. ssum(x) = ∑sum(x) ;
  4. 由于 [10i, 10i+1) 的数长度均为 i +1 ,所以这里 每个 [10i, 10i+1) 里的每个sum(i) 就是一个等差数列,差为 i +1 ;
  5. 所以给定x, ssum(x) = ans[i] + (i+1)∗∑(1,x-i+1) ; ( 10i 为<=x的最大的10的次方数);
  6. ans(i) = (i+1) ∗ ∑(1,区间长度的和)+ ( sum(10i - 1) - sum(10i-1 - 1)) ∗ (x−10i + 1) + ans(i-1);
  7. 两次二分就好了,第一次二分找到最大的 x 使 ssum(x) < k; 则解就在 x+1 块里, k −= ssum(x); 第二次二分找到最大的 x 使 sum(x) < k, 那么解就在数 x+1中,k−=sum(x) , 最后数 x+1 的第 k 位就是答案。
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

inline ll get_sum ( ll x ) { //从1~区间长度x的前x项和

	return x*(x+1)/2;
}

inline ll calc_1 ( ll x ) { // 获取ssum[x]

	ll ans=0, i=1, j=1;
	for ( ; j*10<=x; i++,j*=10 ) {
		ans += i*get_sum(j*9) + i*j*9*(x-j*10+1);
	}
	return ans + i*get_sum(x-j+1);
}

inline ll calc_2 ( ll x ) { // 获取sum[x]

	ll ans=0, i=1, j=1;
	for ( ; j*10<=x; i++,j*=10 ) {
		ans += i*j*9;
	}
	return ans + i*(x-j+1);
}

int main() {

	ll q, k;
	scanf("%lld", &q);

	while (q--) {

		scanf("%lld", &k);
		ll l = 0, r = 1e9, res1, res2;

		while ( l<=r ) { // 第一次二分确定k在哪个块里

            ll mid = l+r >> 1;
            if ( calc_1(mid) < k ) {
                l = mid + 1;
                res1 = mid;
            }
            else r = mid - 1;
		}
		k -= calc_1(res1);
		l = 0, r = res1 + 1;
		while ( l<=r ) { // 第二次二分确定k在哪个数里

            ll mid = l+r >> 1;
            if ( calc_2(mid) < k ) {
                l = mid + 1;
                res2 = mid;
            }
            else r = mid - 1;
		}
		k -= calc_2(res2 ++);

		int a[30];
		memset(a, 0, sizeof(a));
		int i = 0;
        while(res2) {
            a[++i] = res2%10;
            res2 /= 10;
        }
        printf("%d\n", a[i-k+1]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43054397/article/details/101201781