【二分】七颗果子

题目:七颗果子(seven.pas/cpp/in/out)

题目描述

作为一位尚未脱团的神牛,即使是美好七夕之夜也不免孤单。这不,他正独自一人躺在树下看着星星,等着苹果砸呢。

青青不是不知道今晚是什么日子,其实他在盘算着,如何才能飞到鹊桥之上,进而……(以下省略10万字)

“嘭”的一声,青青果断被砸中。青青摸着头,正准备抱怨,不想……“嘭嘭嘭嘭嘭嘭”

……(此处省略200字)

果子1:亲,我这里可是有很多钱的喔~
果子2:亲,我这里的钱是果子1的平方喔~
果子3:亲,我知道果子7有多少钱喔~
果子4:亲,我这里的钱是果子1的四方喔~
果子5:亲,我这里的钱是果子1的五方喔~
果子6:亲,我这里的钱是果子1的六方喔~
果子7:亲,你如果能告诉我果子1有多少钱,我就教你飞上天的法术喔~

……(此处省略200字)

经过死缠烂磨,青青终于买通了果子3,知道了果子7手上有多少钱,下面的事情就交给你了~

数据规模
对于前 3 个数据,数字不超过 2^63 - 1。
对于后 7 个数据,数字不超过 10^777。

输入格式

1 行,1 个整数表示果子 7 有多少钱。

输出格式

1 行,1 个整数表示果子 1 有多少钱。

样例输入

16384

样例输出

4


题目描述完全无法理解。。自己试了一下原来就是求七次根号下一个数。

要用高精。一开始我没有压位,结果就只过了3个点,和他们朴素的一样。


关键点有几个。

1、枚举范围太大了。。其实可以缩小。。计算一下可以确定一个上下界。

2、在求7次方的时候,用快速幂,求出平方,四次方,六次方,每次都要判断一下,如果大于n了就返回大于。

3、压位。


1是最重要的,但是实际上2和3结合起来比1效果更好。。


#include <iostream>
using std::cout;
#define MAX(a,b) ((a)>(b)?(a):(b))

const long bit10 = 100000000;
const long bit = 8;

typedef long long ll;

inline void Plus(ll *a,ll *b,ll *c)
{
	c[0] = MAX(a[0],b[0]);
	
	for (long i=1;i<c[0]+2;i++)
	{
		c[i] = 0;
	}
	for (long i=1;i<c[0]+1;i++)
	{
		if (i > a[0])
			c[i] += b[i];
		else if (i > b[0])
			c[i] += a[i];
		else
			c[i] += a[i]+b[i];
		if (c[i] >= bit10)
		{
			c[i+1] += c[i]/bit10;
			c[i] %= bit10;
		}
	}
	while (c[c[0]+1])c[0] ++;
	while (!c[c[0]] && c[0]>0) c[0] --;
}

inline void Multiply(ll *a,ll *b,ll *c)
{
	c[0] = a[0] + b[0];
	for (long i=1;i<c[0]+2;i++)
	{
		c[i] = 0;
	}
	for (long i=1;i<a[0]+1;i++)
	{
		for (long j=1;j<b[0]+1;j++)
		{
			c[i+j-1] += a[i] * b[j];
			while (c[i+j-1] >= bit10)
			{
				c[i+j] += c[i+j-1] / bit10;
				c[i+j-1] %= bit10;
			}
		}
	}
	while (c[c[0]+1]) c[0]++;
	while (!c[c[0]] && c[0]>0) c[0] --;
}

inline void Div2(ll *a,ll *c)
{
	for (long i=0;i<a[0]+2;i++)
		c[i] = 0;
	for (long i=a[0];i>0;i--)
	{
		c[i-1] += (a[i]&1) * bit10;
		c[i] = (c[i] + a[i]) >> 1;
	}
	c[0] = a[0];
	while (!c[c[0]] && c[0]>0) c[0] --;
}

inline void Increase(ll *a,ll *c)
{
	// from c[0] to a[0] + 2
	
	c[0] = a[0] + 1;
	for (long i=1;i<c[0]+1;i++)
		c[i] = a[i];
	c[c[0]+1] = c[c[0]+2] = 0;c[1] ++;
	for (long i=1;i<c[0]+1;i++)
	{
		if (c[i] >= bit10)
		{
			c[i] %= bit10;
			c[i+1] /= bit10;
		}
		else
		{
			break;
		}
	}
	while (c[c[0]+1]) c[0] ++;
	while (!c[c[0]] && c[0]>0) c[0] --;
}

inline void Decrease(ll *a,ll *c)
{
	c[0] = a[0] + 1;
	for (long i=1;i<c[0]+1;i++)
		c[i] = a[i];
	c[c[0]+1] = c[c[0]+2] = 0;c[1] --;
	for (long i=1;i<c[0]+1;i++)
	{
		if (c[i] < 0)
		{
			c[i] += bit10;
			c[i+1] --;
		}
		else
		{
			break;
		}
	}
	while (!c[c[0]] && c[0]>0) c[0] --;
	while (c[c[0]+1]) c[0] ++;
}

inline void output(ll *a)
{
	cout << a[a[0]];
	for (long i=a[0]-1;i>0;i--)
	{
		if (a[i] < 10000000)
			cout << 0;
		if (a[i] < 1000000)
			cout << 0;
		if (a[i] < 100000)
			cout << 0;
		if (a[i] < 10000)
			cout << 0;
		if (a[i] < 1000)
			cout << 0;
		if (a[i] < 100)
			cout << 0;
		if (a[i] < 10)
			cout << 0;
		cout << a[i];
	}
}

inline long compare(ll *a,ll *b)
{
	if (a[0] != b[0])
		return a[0] < b[0]?-1:1;
	for (long i=a[0];i>0;i--)
		if (a[i] != b[i])
			return a[i] < b[i]?-1:1; 
			
	return 0;
}

ll tmp[100000];
ll MID[100000];


ll times2[100000];
ll times4[100000];
ll times6[100000];
ll times7[100000];

char num[100000];
ll a[100000];
ll L[100000];
ll R[100000];

inline long calc()
{
	Multiply(MID,MID,times2);
	if (compare(times2,a) == 1) return 1;
	Multiply(times2,times2,times4);
	if (compare(times4,a) == 1) return 1;
	Multiply(times2,times4,times6);
	if (compare(times6,a) == 1) return 1;
	Multiply(MID,times6,times7);
	return compare(times7,a);
}

int main()
{
	freopen("seven.in","r",stdin);
	freopen("seven.out","w",stdout);
	
	long _len = 0;
	scanf("%s",num+1);
	while (num[++_len]){
		num[_len]=num[_len]-'0';
		}_len--;
	
	for (long i=1;i<_len+1;i++)
	{
		a[(_len-i)/bit+1] = a[(_len-i)/bit+1]*10+num[i];
	}
	a[0] = (_len-1)/bit+1;
	
	L[0] = 1;
	L[1] = 0;
	Decrease(a,R);
	
	while (compare(L,R) < 1)
	{
		Plus(L,R,tmp);
		Div2(tmp,MID);
		
		long rs = calc();
		if (rs == -1)
		{
			Increase(MID,L);
		}
		else if (rs == 1)
		{
			Decrease(MID,R);
		}
		else
		{
			output(MID);
			break;
		}
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值