Description
This problem involves the efficient computation of integer roots of numbers.
Given an integer n>=1 and an integer p>= 1 you have to write a program that determines the n th positive root of p. In this problem, given such integers n and p, p will always be of the form k to the n th. power, for an integer k (this integer is what your program must find).
Input
Output
Sample Input
2 16 3 27 7 4357186184021382204544
Sample Output
4 3 1234
Source
剛拿到這道題,想到的就是高精度加上二分。敲完後TLEandWA了好幾次,一直改了兩三個小時,還是ac不了。無奈之下,看了一下discuss。頓時無奈了。數據是有問題的,有可能存在無解的情況,所以使用高精度+二分去做的話,還要特殊處理一下,如果存在無解,則輸出偏小的那個值。最後終於ac了。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int maxn = 21;
const int mod = 1e8;
int n;
string p;
long long now[maxn], form[maxn]; //1e8 for each
void run(int fac)
{
for(int i = 0; i < 20; ++i) now[i] *= (long long)fac;
for(int i = 0; i < 20; ++i) {
now[i+1] += now[i] / mod;
now[i] = now[i] % mod;
}
}
int judge(int x)
{
memset(now, 0, sizeof(now));
now[0] = x;
for(int i = 1; i < n; ++i) {
run(x);
for(int j = maxn - 1; j >= 0; --j) {
if(now[j] > form[j]) return 1;
else if(now[j] < form[j]) break;
}
}
for(int i = maxn-1; i >= 0; --i) {
if(now[i] > form[i]) return 1;
else if(now[i] < form[i]) return -1;
}
return 0;
}
int main()
{
// freopen("in", "r", stdin);
while(cin >> n >> p) {
memset(form, 0, sizeof(form));
int cur = 0, bit = 1;
for(int i = p.length()-1; i >= 0; --i){
if(bit >= mod) {bit = 1; cur++;}
form[cur] += bit * (p[i]-'0');
bit *= 10;
}
int l = 1, r = 1e9;
int ok = 0;
while(l <= r) {
int m = (r+l) / 2;
int c = judge(m);
// printf("m = %d c=%d l=%d r=%d\n", m, c, l, r);
if(c == 0) {printf("%d\n", m); ok = 1; break;}
else if(c > 0) r = m - 1;
else if(c < 0) l = m + 1;
}
if(!ok) printf("%d\n", l-1);
}
return 0;
}
最後附上另一個博主的文章。
转载请注明出处:優YoU http://user.qzone.qq.com/289065406/blog/1299228474
提示:
一般思路:二分+高精度算法
但是本题还有一个更加巧妙的办法去处理:
首先需要明确:double类型虽然能表示10^(-307) ~ 10^308, (远大于题意的1<=p<10101这个范围),但只能精确前16位,因此必须慎用!
那么为了避免double对输入的数在运算过程中进行精确,那么我们必须让double的运算第一步就得到一个int(即小数点尾数全为0),这个不难理解。
然后根据题意,是求指数k,一般人自然想到利用 对数log,即k=lognp。但是不要忘记使用对数最大的问题就是没有lognp函数,只有log()函数(底数为e),为此要计算lognp就必须使用换底公式lognp=log(p)/log(n),即k= log(p)/log(n),由于这使得double的运算变为了3次,而且执行除法前的两次对数运算log的结果未必都是int,很显然k是一个被精确了的double
很多人到这里就放弃了使用double,转换方向到正常思路(二分+高精度算法),但是不要忘记求指数k除了使用对数log,还能使用指数的倒数开n次方,这时就可以用pow函数了
k=pow(p,1.0/n),double的运算一步到位,k自然也是一个int
//Memory Time
//280K 0MS
#include<iostream>
#include<math.h>
using namespace std;
int main(void)
{
double n,p;
while(cin>>n>>p)
cout<<pow(p,1.0/n)<<endl; //指数的倒数就是开n次方
return 0;
}
哈哈,不要惊讶!程序就是这么短,这就是“技巧”与“算法”的差别
用double避开高精度算法,可以说是这题最大的BUG O(∩_∩)O
還有另外一種二分,不需要使用高精度
#include<stdio.h>
#include<math.h>
#define eps 0.0000000001
void init(), work();
double n, m, k;
int main()
{
init();
return 0;
}
void init()
{
while(scanf("%lf %lf", &n, &m) != EOF)
work();
}
void work()
{
long long left, right, mid;
left = 0;
right = 1000000002;
while(left + eps < right){
mid = (left + right) / 2;
if(pow(mid, n) - m > 0)
right = mid;
else
if(pow(mid, n) - m < 0)
left = mid;
else{
printf("%.0ld\n", mid) ;
break;
}
}
}
类型 长度 (bit) 有效数字 绝对值范围
float 32 6~7 10^(-37) ~ 10^38
double 64 15~16 10^(-307) ~10^308
long double 128 18~19 10^(-4931) ~ 10 ^ 4932