Number Sequence poj1019(数学方法)

Number Sequence
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 39182 Accepted: 11366

Description

A single positive integer i is given. Write a program to find the digit located in the position i in the sequence of number groups S1S2...Sk. Each group Sk consists of a sequence of positive integer numbers ranging from 1 to k, written one after another. 
For example, the first 80 digits of the sequence are as follows: 
11212312341234512345612345671234567812345678912345678910123456789101112345678910

Input

The first line of the input file contains a single integer t (1 ≤ t ≤ 10), the number of test cases, followed by one line for each test case. The line for a test case contains the single integer i (1 ≤ i ≤ 2147483647)

Output

There should be one output line per test case containing the digit located in the position i.

Sample Input

2
8
3

Sample Output

2
2

一个序列1 12 123 1234 12345 123456 1234567 12345678 123456789 12345678910 1234567891011 ---------

空格是为了明显分组概念的

问题是求第n个数的数字是什么  

就是一位数


关键点

1.一个数的位数可以用对数求 例如 10:log10(10)+1 = 2,1:log(1)+1 = 1;

(注 求大数阶乘的位数也可以用对数

2.关于函数的重载以及类型转换

double log(double)
float log(float)
double pow(double , double)
float pow(float ,float)

具体见代码  (主要是理解分组概念/一个数字位的log10对数的应用)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>

using namespace std;

#define For(i,a,b) for(i=a;i<=b;i++)
#define Out(x) cout<<x<<endl
#define ll long long
const ll inf = 40000;

ll num[inf];
ll sum[inf];

void reset()
{
    sum[0]=0;
    num[1]=1;
    sum[1]=1;
    int i;
    For(i,2,inf)
    {
        num[i]= num[i-1] + (ll)log10((double)i)+1;  ///以1开头为一组,第i组数共多少位数字
        sum[i] = num[i] + sum[i-1];                 ///前i组的序列长度
    }
}

ll digit2(ll n)     ///两个digit函数大致相同
{                   ///区别 len大于/小于pos
    int i;
    ll len,pos;

    For(i,1,inf)
    {
        if(sum[i] >= n) ///等于的时候会出现pos=0
        {               ///当sum[i] == n 时 需要跳出 否则会出现len>pos
            break;
        }
    }
    pos = n-sum[i-1];  ///pos表示在第i组中的位置
    len=0;
    if(pos <= 9)       ///小技巧 1~9 直接打印,都是个位数
    {
        return pos;
    }
    For(i,1,inf)
    {
        len+=(ll)log10((double)i)+1;   ///在第i组中的长度
        if(len+(ll)log10((double)(i+1))+1 >= pos)  ///当找到比pos大于等于的i后跳出
        {
            break;
        }
    }
    int x = (ll)log10((double)(i+1))+1-(pos-len);  ///此处与digit不同
    return ((i+1)/(ll)pow(10.0,x))%10;   
    ///类比下面的digit函数
    ///假设:序列为--123124125--
    ///那么len在pos前123 3位置 
    ///pos在124 2位置
    ///则此时是i=123 i+1=124
    ///x是需要截取的数字的位数 需要把124的4去掉后取余
    ///即除以10的(124(i+1)的数字长度)-(pos-len))次方 (例x=1)
    ///主要是判断到底i在哪 pos位置指向哪个数字
}

ll digit(ll n)
{
    int i;
    ll len,pos;
    For(i,1,inf)
    {
        if(sum[i] >= n) ///等于的时候会出现pos=0
        {
            break;
        }
    }
    pos = n-sum[i-1];
    ///pos在第i组的位置 因为上if判断所以避免了出现pos=0
    len=0;
    for(i=1; len<pos; i++)
    {
        len+=(ll)log10((double)i)+1;
    }
    int x = len-pos;
    return ((i-1)/(ll)pow((double)10.0,x))%10;
    ///假设:序列为--122123124--
    ///len在123 3位置
    ///pos在123 2位置
    ///则此时是i=124 i-1=123
    ///x是需要截取的数字的位数 需要把123的3去掉后取余
    ///即除以10的len-pos方(例x=1)
    ///主要是判断到底i在哪 pos位置指向哪个数字
}
int main()
{
    reset();
    ll t,n;
    cin>>t;
    while(t--)
    {
        cin>>n;
        Out(digit(n));
    }
    return 0;










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值