题面
题目描述
输入一个正整数n(1<=n<=10^100),输出n的平方根的整数部分。
输入
正整数n
输出
n的平方根的整数部分
样例输入
10
样例输出
3
数据范围
对于100%数据 n<=10^100
题解
恶心的大高精度……
我记得数学书上好像介绍过一种快速求开方的办法,但我忘了,只记得和20有关,忘了不要紧,自己推一遍(考时花了15分钟才推出来,代码还因为人懒特别长,真实代码只用120行左右)
操作大致如图
图片先空着
现在我们尝试考虑给已经求出的平方根再加一位造成的影响。
先尝试从 n = x ‾ 2 n=\overline{x}^2 n=x2变成 100 n = x y ‾ 2 100n=\overline{xy}^2 100n=xy2( x y ‾ \overline{xy} xy表示一个数由xy组成,比如此时若 x = 1 x=1 x=1, y = 2 y=2 y=2,则 x y ‾ \overline{xy} xy=12)
则若
n
=
x
‾
2
n=\overline{x}^2
n=x2,显然此时
n
=
x
2
n=x^2
n=x2
若
100
n
=
x
y
‾
2
100n=\overline{xy}^2
100n=xy2,那么可得
100
n
=
(
10
x
+
y
)
2
100n=(10x+y)^2
100n=(10x+y)2
继续往下推:
100
n
=
100
x
2
+
20
x
y
+
y
2
100n=100x^2+20xy+y^2
100n=100x2+20xy+y2
因为n的系数乘以了100,所以第一个式子中的
x
2
x^2
x2与第二个式子中的
100
x
2
100x^2
100x2可以抵消,结果只是增加了
20
x
y
+
y
2
=
y
(
y
+
20
x
)
20xy+y^2=y(y+20x)
20xy+y2=y(y+20x)
其中
20
x
20x
20x的值已知,且y最多等于9,枚举一下y就好了
所以时间复杂度只有
O
(
9
)
O(9)
O(9)(不计算高精度操作)
拿一个数实际操作一下令此时n=1000
则显然对其进行分组得到
10
00
10\ 00
10 00
先对前两位进行处理,得到其平方根的第一位为3,
现在对后两位进行处理,则此时的
n
=
1000
−
900
=
100
n=1000-900=100
n=1000−900=100
枚举y则
y
(
y
+
60
)
<
=
100
y(y+60)<=100
y(y+60)<=100
所以y最大等于1
故答案为31。
最终时间复杂度约为
O
(
(
l
o
g
10
n
2
)
2
∗
10
)
O((\frac{log_{10}n}{2})^2*10)
O((2log10n)2∗10),可以轻松跑过去。
只是码量有点大。
代码
#include<bits/stdc++.h>
using namespace std;
struct node
{
int num[2005],len;
}re,ans,de,mot,pl;
int i,j,n,m,k,l,o,p;
char s[2005];
void mem(node &qs)
{
qs.len=0;
memset(qs.num,0,sizeof qs.num);
}
void moti(int w)
{
mem(mot);
node pre=ans;
pre.len=w;
for (int x=1;x<=pre.len/2;x++)
{
swap(pre.num[x],pre.num[pre.len-x+1]);
}
for (int x=1;x<=w;x++)
{
for (int y=1;y<=w;y++)
{
mot.num[x+y-1]+=pre.num[x]*pre.num[y];
mot.num[x+y]+=mot.num[x+y-1]/10;
mot.num[x+y-1]%=10;
}
}
mot.len=i*2;
while (mot.num[mot.len]==0&&mot.len>0)
{
mot.len--;
}
for (int x=1;x<=mot.len/2;x++)
{
swap(mot.num[x],mot.num[mot.len-x+1]);
}
mot.len+=2;
}
void plu(int a,node b)
{
node c;
mem(pl);
mem(c);
for (int x=1;x<=b.len;x++)
{
c.num[x]=b.num[b.len-x+1];
}
c.num[1]+=a;
for (int x=1;x<=b.len+1;x++)
{
mot.num[x+1]+=mot.num[x]/10;
mot.num[x]%=10;
}
if (c.num[b.len+1]==0) pl.len=b.len;
else pl.len=b.len+1;
for (int x=1;x<=pl.len;x++)
{
pl.num[x]=c.num[x];
}
}
void moqt(int w,node z)
{
mem(mot);
plu(w,z);
for (int x=1;x<=pl.len+2;x++)
{
mot.num[x]+=pl.num[x]*w;
mot.num[x+1]+=mot.num[x]/10;
mot.num[x]%=10;
}
mot.len=i+2;
while (mot.num[mot.len]==0&&mot.len>0)
{
mot.len--;
}
for (int x=1;x<=mot.len/2;x++)
{
swap(mot.num[x],mot.num[mot.len-x+1]);
}
}
void mo(int w)
{
mem(mot);
node yx;
mem(yx);
for (i=1;i<=w;i++) yx.num[i]=ans.num[w-i+1];
for (int x=1;x<=w+2;x++)
{
mot.num[x]+=yx.num[x]*20;
mot.num[x+1]+=mot.num[x]/10;
mot.num[x]%=10;
}
mot.len=i+2;
while (mot.num[mot.len]==0&&mot.len>0)
{
mot.len--;
}
for (int x=1;x<=mot.len/2;x++)
{
swap(mot.num[x],mot.num[mot.len-x+1]);
}
}
void dec(int w,node a)
{
mem(de);
node xs;
mem(xs);
xs.len=w*2-(re.num[1]<10);
int xq=xs.len%2;
for (int x=1;x<=xs.len;x++)
{
if ((x+xq)%2)
{
xs.num[x]=re.num[(x+xq+1)/2]/10;
}
else
{
xs.num[x]=re.num[(x+xq)/2]%10;
}
}
for (int x=1;x<=xs.len;x++)
{
de.num[x]=xs.num[xs.len-x+1]-a.num[max(0,a.len-x+1)];
if (de.num[x]<0)
{
de.num[x]+=10;
xs.num[xs.len-x]--;
}
}
int x;
for (x=xs.len;x>=1;x--)
{
if (de.num[x]!=0) break;
}
de.len=x;
for (x=1;x<=de.len/2;x++)
{
swap(de.num[x],de.num[de.len-x+1]);
}
}
int pd(node a,node b)
{
if (a.len>b.len) return 1;
if (a.len<b.len) return 0;
for (int x=1;x<=a.len;x++)
{
if (a.num[x]>b.num[x]) return 1;
if (a.num[x]<b.num[x]) return 0;
}
return 0;
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
if (n%2)
{
re.len=1;
re.num[1]=s[1]-'0';
for (i=2;i<=n;i++)
{
if (i%2==0)
{
re.num[++re.len]=s[i]-'0';
}
else
{
re.num[re.len]*=10;
re.num[re.len]+=s[i]-'0';
}
}
}
else
{
re.len=1;
re.num[1]=s[1]-'0';
for (i=2;i<=n;i++)
{
if (i%2==1)
{
re.num[++re.len]=s[i]-'0';
}
else
{
re.num[re.len]*=10;
re.num[re.len]+=s[i]-'0';
}
}
}
m=re.len;
for (i=1;i<=m;i++)
{
moti(i-1);
dec(i,mot);
mo(i-1);
node q=mot;
for (j=1;j<=9;j++)
{
moqt(j,q);
if (pd(mot,de)) break;
}
ans.num[i]=j-1;
}
for (i=1;i<=m;i++)
{
printf("%d",ans.num[i]);
}
}
Ps:代码又臭又长,实在不想注释了,大家凑合着看吧(那几个moti,mo,moqt什么的全部都是高精乘,人懒就没合并)