poj 1019

题意大致是:在一个序列里面,每个元素都是个数字序列,若该数字序列序号为j,则该序列就是1234.....j;输出这个总序列的第i个数字。

     先把前八十个字符写出来:1 12 123 1234 12345 123456 1234567 12345678 123456789 12345678910 1234567891011 12345678910 可以看出每个元素序列所具有的数字个数为该元素序列的位序加一。我的思路是这样的:先找出的i个数字所在的元素序列N,然后再找出第i个数字在第N个元素序列的第M个数字里,然后再求出是M里的那个字符个数字。代码如下:

#include<stdio.h>
#include<math.h>
__int64 f1(__int64 s)//求出前s个元素序列的所有数字位数和
{
	int i;
	__int64 a,k,n,sum=0;
	k=9;a=1;//a为首项
	for(i=1;k<=s;i++,k*=10)//分段求出每一段的数字位数和
	{
		sum=sum+k*a+k*(k-1)/2*i;//i为公差,k为项数
		a=a+i*k+1;//更新首项
	}
	n=s-k/9+1;//不够一段的个数
	sum=sum+n*a+n*(n-1)/2*i;//总位数
	return sum;
}
__int64 f2(__int64 s)//每个元素序列前n位数字的位数
{
	int i,count=0;
	__int64 k,sum=0,w=1;
	k=s;
	while(k)//确定该数字位数
	{
		count++;
		k=k/10;
        w=w*10;
	}
	k=9;
    for(i=1;i<count;k*=10,i++)
		sum=sum+k*i;//分段求和
    sum=sum+(s-w/10+1)*count;//再加上不够一段的位数和
	return sum;
}
int main()
{
	int n,count,w;
	__int64 m,low,high,mid,k,s;
	scanf("%d",&n);
	while(n--)
	{
		scanf("%I64d",&m);
		low=1;high=m;
		while(low<high)//二分查找该位数所在的元素序列
		{
			mid=(low+high)/2;
			k=f1(mid);
			if(k>m && f1(mid-1)>=m)
				high=mid-1;
			else if(k<m)
				low=mid+1;
			else 
				low=high=mid;
		}
		k=f1(low-1);
		k=m-f1(low-1);
		low=1;high=k;
		while(low<high)//确定元素序列后再用二分查找确定该位数在该元素序列的哪个数字内
		{
			mid=(low+high)/2;
			s=f2(mid);
			if(s>k && f2(mid-1)>=k)
				high=mid-1;
			else if(s<k)
				low=mid+1;
			else
				low=high=mid;
		}
		m=f2(low-1);
		m=k-f2(low-1);
		k=low;count=0;w=1;
		//确定数字后再确定该数字的那一位
		while(k>=10)
		{
			count++;
			k=k/10;
			w=w*10;
		}
        while(m--)
		{
			k=low/w;
			low=low%w;
			w=w/10;
		}
		printf("%I64d\n",k);
	}
	return 0;
}

posted @ 2011-07-18 17:12  书山有路,学海无涯  阅读(1468)  评论(0编辑  收藏  举报