POJ 2109 Power of Cryptography二分+大数相乘和pow为什么可以直接过的原因

时间:2023-02-02 15:06:55

http://poj.org/problem?id=2109

这个题目一看就是个大数的题目,但是很流行下面这段一行代码的程序:

 

#include<stdio.h>
#include<math.h>
#include<iostream>
using namespace std;
int main()
{
double a,b;
while(cin>>a>>b)
{
cout<<pow(b,1.0/a)<<endl;
}
return 0;

 

}

虽然这段代码经过无数次提交ac的证明,但是我不得不说,不懂,完全不懂,double 能把100位的n按照合理精度转下来吗?、、

也许这个代码能够ac,是因为测试数据不严谨吧。

先介绍正解,然后在说明原因。

下面,介绍正解:

二分+大数运算

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;


#define M 400
#define mem0(f) memset(f,0,sizeof(f))
//先写大数相乘
char res[M],nn[M];
int k;
void times(char*a,char*b,char*s)
{
char aa[M],bb[M];
mem0(aa);
mem0(bb);
int l1=strlen(a),l2=strlen(b);
for(int i=0;i<l1;i++)
{
aa[i]=a[l1-i-1]-'0';
}
for(int i=0;i<l2;i++)
{
bb[i]=b[l2-1-i]-'0';
}
int n=0;
for(int i=0;i<l1;i++)
{
n=i;
for(int k=0;k<l2;k++)
{
s[n]+=aa[i]*bb[k];
if(s[n]>9)
{
s[n+1]+=s[n]/10;
s[n]=s[n]%10;
}
n++;
}
}
if(s[n])n++;
for(int i=0;i<n;i++)
{
s[i]+='0';
}
char t;
for(int i=0;2*i<n;i++)
{
t=s[i];
s[i]=s[n-1-i];
s[n-i-1]=t;
}
}
void power(char*a,int n,char*s)
{
char sm[M];
strcpy(s,"1");
for(int i=0;i<n;i++)
{
mem0(sm);
//cout<<n<<endl;
times(s,a,sm);
strcpy(s,sm);
//puts(s);
}
//strcpy(s,sm);
}
int cmp(char*a,char*b)
{
int l1=strlen(a),l2=strlen(b);
if(l1>l2)return 1;
else if(l1<l2)return -1;
else
{
for(int i=0;i<l1;i++)
{
if(a[i]>b[i])return 1;
else if(a[i]<b[i])return -1;
}
return 0;
}
}
int minn,maxx,n,ok,cen;
char mid[M];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d%s",&k,nn))
{
ok=0;
mem0(mid);
mem0(res);
n=ceil(((float)strlen(nn)/k));
//puts(nn);
minn=(int)pow(10,n-1);
maxx=(int)pow(10,n)-1;
//cen=(minn+maxx)/2;
while(!ok)
{
mem0(res);
mem0(mid);
cen=(minn+maxx)/2;
sprintf(mid,"%d",cen);
power(mid,k,res);
if(cmp(res,nn)<0)
{
minn=cen+1;
}
else if(cmp(res,nn)>0)
{
maxx=cen-1;
}
else
{
ok=1;
}
if(minn>maxx)break;
}

//这个题目说总会存在k是逗我们玩的,其实就是找满足k的p次方小于或等于n的最大k值,这也是为什么pow可以直接过的原因之一。

//如果没有下面在无解时的近似处理,结果是WA,实验过了,加上这段代码的话就可以AC,果断测试数据坑人有木有!
if(!ok)
{
while(cmp(res,nn)>0)
{
cen--;
sprintf(mid,"%d",cen);
mem0(res);
power(mid,k,res);
}
}
printf("%d\n",cen);
}
return 0;
}

上面对代码的说明解释了pow可以直接过的部分原因:反正是要求一个最大值,根本就不需要存储所有的精度。

深层分析如下

首先,题目中的数据强度并不弱,这一点确实如题目中所说:“For all such pairs 1<=n<= 200, 1<=p<10101,所以,double型是不能精确地表示出所给数据,但是却能表示出一个近似值。

当向double型变量中存入

4357186184021382204544

然后再输出,得到的是

4357186184021382000000

后六位的值变为了0,这一点和int型变量是有很大区别的。也就是说当存入double型变量的值超出了它的精度表示范围时,将低位的数据截断。(关于浮点数在计算机中的表示方法,百度吧…讲的蛮清楚的。)

在本题中,如果测试数据为:

7     4357186184021382204544

实际上所处理数据是:

7     4357186184021382000000

拿4357186184021382000000开7次方的结果自然就是1234。

为什么不是1233或者1235呢?

12337=4332529576639313702577

12347=4357186184021382204544

12357=4381962969567270546875

可以看出在double型所能表示的精度范围内,它们三个值已经不同了。

所以,此题中的测试数据也都是类似于上述情况,所以才能使用double型开n次方的方法。

关于此,感谢

CSDN的PangWD_C
更多及原文请访问http://blog.csdn.net/code_pang/article/details/8263971