这道题目不难但是很麻烦,而且题意容易误解。一开始我就没有完全搞懂题意,导致错误。最容易出错的地方在超过1位数的数字占用位数不再是1了,而是其数字个数,这也应该不算什么难点,只是容易搞成所有数字都是一位(一时就脑袋蒙了),若所有数字都是1位,那么就简单多了,直接用求和公式k*(1+k)/2,求出S1+S2+……+SK的所有数字个数,然后稍加处理即可。而这里不同数字要按照实际位数计算。不过分析思路还是同上。只是处理起来要麻烦多了。
分析如下:
可以二分查找出小于或等于n(即题目输入的i值)的k,即 S1+S2+……+SK<=n,具体计算方法稍后说明。这里有两种情况。
1)若为相等,那么第n位即为数字K的最后一位,直接输出
2)若小于,那么第n位必定为数字K+1序列数字中的某一位。
情形2)更复杂,也可以分两种情况:
i)当第n位数字为刚好1位数,2位数,……,k位数的结束位时,则必定为9
ii)否则,第n位数字为这些位中某个数的中间一位数字
而第ii)种情况又可以细分为如下两种情况:
iii 1)第n为数字为数字的末尾位,特殊处理
iii 2)否则,一般处理
这里详细介绍一下上面所述的关于如何计算S1+S2+……+Sk的问题。
这里设置两个数字first,和 num 。分别记录K位数的第一个数字和K位数的个数。例如1位数的第一个数字为1,个数为9,2位数第一个数字为10,个数为90,等等。
那么SK的计算方法为求出所有位数小于K数字位数的数字个数与它们各自位数的乘积之和(即位数小于K位数的所数字位数之,利用上面所述的两个数组),
然后加上(K-first[len(K)]+1)* len(K)。依次求出S1,S2,……SK的值,然后相加。
上述过程可以抽象为一个函数。
下面是代码:132K+94MS
#include <stdio.h>
#include <stdlib.h> //初始化first与num数组
int first[11]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000};
int num[11]={0,9,90,900,9000,90000,900000,9000000,90000000,900000000};
int Case;
__int64 n; // 输入值i
__int64 record;
bool trag; // 标记量
__int64 Get_num(int number){ // S1+S2+S3+……SK求和
__int64 pro=0; // 保存结果
for(int j=1;j<=number;j++){ // 循环求解SK
int i,len=0,temp=j;
while(temp>0){ // 求解数字位数
len++;
temp/=10;
}
for(i=1;i<len;i++) // 累加比该位数小的所有数字位数之和
pro+=num[i]*i;
pro+=(j-first[len]+1)*len; // 加上该位数的数字位数之和
}
return pro; //返回求和结果
}
int Binary_search(int left,int right){ // 二分查找Sk<=n
while(left<=right){
int mid=(left+right)>>1;
if(Get_num(mid)<n)
left=mid+1;
else if(Get_num(mid)>n)
right=mid-1;
else{
trag=1; //若相等
return mid;
}
}
trag=0; // 小于
return right; // 注意此处为right而不是left,这样才能保证Sk<n
}
int main(){
scanf("%d",&Case); //case数
while(Case--){
scanf("%I64d",&n);
int Count=Binary_search(1,35000); //经过测试35000是比较合适的数字,保证二分查找的正确性
if(trag){ // 若相等则直接输出最后一位数字
printf("%d\n",Count%10);
continue;
} //否则判断是在末尾位还是中间位
record=n-Get_num(Count);
//printf("%I64d ====\n",record);
int pivot=1;
while(true){
if(record-num[pivot]*pivot>0){ //若还有多余的位数,则继续分解
record-=num[pivot]*pivot;
pivot++;
}
else if(record-num[pivot]*pivot<0){ //若没有多余的位数了,则设置标记为0,为中间位
trag=0;
break;
}
else{ // 若正好相等,则为结束位,设置标记为1
trag=1;
break;
}
}
if(trag){ //若为结束位,直接输出9
printf("9\n");
continue;
}
if(record%pivot==0){ // 判断若属于中间数字的最后一位,则特殊处理
int x=first[pivot]+record/pivot-1;
printf("%d\n",x%10);
continue;
} //否则一般处理
int x=first[pivot]+record/pivot;
int index=pivot-record%pivot+1;
pivot=1;
while(x>0){
if(pivot==index){
printf("%d\n",x%10);
break;
}
x/=10;
pivot++;
}
}
return 0;
}
也给出误解题意的程序(仅作参考)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int Case;
__int64 n;
bool trag;
int Binary_search(int left,int right){
while(left<=right){
int mid=(left+right)>>1;
if(n<(mid+1)*mid/2)
right=mid-1;
else if(n>(mid+1)*mid/2)
left=mid+1;
else{
trag=1;
return mid;
}
}
trag=0;
return right;
}
int main(){
scanf("%d",&Case);
while(Case--){
scanf("%I64d",&n);
int temp=Binary_search(1,2*int(sqrt(double(n)))+1);
if(!trag)
printf("%I64d\n",n-(1+temp)*temp/2);
else
printf("%d\n",temp);
}
return 0;
}