UVa 10706 Number Sequence

/*
思路: 二分法求出k值, 再对sk二分法求出所在数值, 最后确定所在位
*/
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
int s1[] = {0, 45, 9045, 1395495, 189414495}; // 前0项 前9项 前99项 前999项 前9999项位数和
int s2[] = {0, 9, 189, 2889, 38889}; // s0 s9 s99 s999 s9999的位数
int s3[] = {0, 9, 99, 999, 9999};

void getxy(int &x, int &y, int d)
{
    x = 1;
    y = 9;
    while(d-->1) {
        x *= 10;
        y = y*10 + 9;
    }
}

void guess(int n, int d)
{
    int x, y;
    getxy(x, y, d); //获得二分法上下界

    //二分法算出k值(存在x中)
    double sum = n - s1[d-1]; // sum开始计算的位数
    int a = s3[d-1]; //a, b, sf 求和参数
    int b = s2[d-1];
    int sf = b + d; //sf第一项位数
    double s;
    while(x < y) {
        int m = x + (y-x)/2;
        int sm = (m - a)*d + b;
        s = floor(1.0*(sm+sf)*(m-a)/2 + 1e-9); //等差数列求和公式
        if(abs(s - sum) < 1e-9) {
            x = m; break;
        }
        if(s - sum > 1e-9) y = m;
        else x = m+1;
    }
    int sb = (x-a-1)*d + b; //x的前一列 s(k-1)所有位数
    s = floor(1.0*(sb+sf)*(x-a-1)/2 + 1e-9);
    int left = floor(sum - s + 1e-9);
    for(d=0; d<5; d++) {
        if(left <= s2[d]) break;
    }
    left -= s2[d-1];
    //二分法算出Sk中的第几项
    getxy(x, y, d);
    int cnt;
    int sx = x;
    while(x < y) {
        int m = x + (y-x)/2;
        cnt = (m-sx+1)*d; //等差数列求和公式
        if(cnt == left) {
            x = m; break;
        }
        if(cnt > left) {
            y = m;
        } else {
            x = m+1;
        }
    }
    //判断在x第几位
    cnt = left - (x-sx)*d -1;
    char buff[6];
    sprintf(buff, "%d", x);
    printf("%c\n", buff[cnt]);
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    #endif
    int T;
    scanf("%d", &T);
    int n;
    while(T--) {
        scanf("%d", &n);
        int d;
        for(d=0; d<5; d++) {
            if(n <= s1[d]) break;
        }
        guess(n, d);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值