JZOJ 3474. 【NOIP2011初赛】大整数开方

题面

题目描述

输入一个正整数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=1000900=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)210),可以轻松跑过去。
只是码量有点大。

代码

#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什么的全部都是高精乘,人懒就没合并)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值