试题 历届试题 小数第n位 循环小数的循环节
时间限制:1.0s 内存限制:256.0MB
问题描述
我们知道,整数做除法时,有时得到有限小数,有时得到无限循环小数。
如果我们把有限小数的末尾加上无限多个0,它们就有了统一的形式。
本题的任务是:在上面的约定下,求整数除法小数点后的第n位开始的3位数。
输入格式
一行三个整数:a b n,用空格分开。a是被除数,b是除数,n是所求的小数后位置(0<a,b,n<1e9)
输出格式
一行3位数字,表示:a除以b,小数后第n位开始的3位数字。
样例输入
1 8 1
样例输出
125
样例输入
1 8 3
样例输出
500
样例输入
282866 999000 6
样例输出
914
思路1【15ms做法】:用x和y表示分子和分母,先求出整数部分x div y和余数部分x mod y,那么小数部分就是重复将余数部分乘以10再进行整除运算和求余运算,直到出现循环或余数为0时结束。不妨从余数入手,因为小数部分整除的结果是由上一次运算的余数决定的,如果某一次运算产生的余数跟前面产生的余数相同,则说明循环开始。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6+10;
ll p[maxn],q[maxn],l[maxn]; //分别标记余数是否出现,存放小数结果和循环部分
ll a,b,n;
int main()
{
scanf("%lld %lld %lld",&a,&b,&n);
ll rest = a%b; //余数
for(int i=0; i<maxn; i++) {
p[i] = -1;
q[i] = 0;
}
ll len = 0;
while(rest!=0 && p[rest]==-1) {
// 余数不为 0(未除尽)并且第一次出现
p[rest] = len++; // 标记余数为第几个,p[rest]为不循环部分的长度
rest *= 10;
q[len] = rest/b; // 小数的下一位,q[1]为小数后第一位,len可以看作结果的长度
rest %= b; //余数更新
}
// 若是因为rest==0而退出循环,表示除得尽
// 若是因为p[rest]!=-1退出,说明出现了重复的余数,除不尽
if(rest==0) {
p[rest] = len;
q[++len] = 0;
}
int len_nl = p[rest]; //不循环的长度
int len_l = len-len_nl; //循环长度=总长度-不循环长度
if(n>len_nl) {
//输出的位置在循环部分
l[0] = q[len]; // 为方便输出,将循环部分放进l[],将其最后一位放在l[0]
for(int i=1; i<len_l; i++)
l[i] = q[len_nl+i];
n -= len_nl; //因为是输出循环部分的数字,所以砍掉前面的
cout<< l[n%len_l] << l[(n+1)%len_l] << l[(n+2)%len_l] <<endl;
}
else if(n+2<=len_nl) cout<<q[n]<<q[n+1]<<q[n+2]<<endl;
else if(n+1==len_nl) cout<<q[n]<<q[n+1]<<q[(n+2)%len_l]<<endl;
else if(n==len_nl) cout<<q[n]<<q[(n+1)%len_l]<<q[(n+2)%len_l]<<endl;
return 0;
}
思路2【156ms做法】:模拟一下除法,但是如果直接一个个相除,时间复杂度会过高,此时应该想办法加速:每次循环后乘以10的n次方,同时小数快速前进n位!(思路更加清晰,代码更加简单,就是时间增长到了近十倍 QAQ)
#include <bits/stdc++.h>
#define ll long long
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std;
int main()
{
ll a,b,n;
while(cin>>a>>b>>n)
{
while(n-10>0)
{
a *= 1e10; //快速逼近
a %= b;
n -= 10;
}
for(int i=1; i<n+3; i++)
{
a *= 10;
if(i>=n) cout<<a/b;
a%=b;
}
}
puts("");
return 0;
}