算法导论F 快速幂(快速幂,OJ里的一点小坑点)

题目:

题目描述



给定x,求f(x)对100000007取余的结果。

输入

多组测试样例,最多50组。每组测试样例给定一个整数x(1<=x<=25000)

输出

对每个样例,输出一行,代表f(x)对100000007取余的结果。
 

样例输入 Copy
3
4
5
样例输出 Copy
33
289
3414

解析:

对于求pow(i,i)阶段,很明显套用快速幂的模板即可。

对于累加阶段,可以暴力求累加,就是每给出一个x,就求出pow(1,1)到pow(x,x)的累加和,这样是不会超时的,但最好还是用上一点动态规划的思想,用ans[i]数组存储从pow(1,1)到pow(i,i)的累加和,这样ans[i+1] = ans[i]+pow(i+1,i+1)。

当然在快速幂模板中不要忘记随时对中间结果根据题目要求进行取余操作,只对最后结果取余会造成中间结果超限的。

oj中的小坑点

学校的oj在输入数据的时候是不会提供结束符的,因此习惯用while(!cin.eof())来进行不停输入的人会出现答案错误的情况,只需要换成while(cin>>x)即可。

快速幂模板:

快速幂的思想为:

底数为a,指数为b,变量ans初始化为1记录a的b次方的结果

当指数为偶数时,指数除以2,底数自己乘自己

当指数为奇数时,指数减一,ans 乘 底数

就这样不断循环直到指数为0

这样的算法会使循环的次数大大降低,一个很大的指数可能经过不到10次循环就能求出结果,而如果用暴力解法或者调用pow函数,一百亿次方就需要循环一百亿次,时间多长可想而知。

下面为比较容易理解的一种模板写法:

long long fpow(long long a,long long b)
{
	long long ans=1;
	while(b)
	{
		if(b%2==0)
		{
			b/=2;
			a*=a; 
		}
		else
		{
			ans*=a; 
			b--;
		}
	} 
	return ans;
}

下面是精简版,也是最常见的一种写法,运用到了一些位运算的知识以求精简代码

long long fpow(long long a,long long b){
	long long ans=1;
	while(b){
		if(b&1)//b的二进制最后一位与1相与,如果b最后一位是0,那么b是偶数,与1相与为false,反之为true 
			ans = ans*a;//b为奇数ans就乘a,b--的步骤可以省略,原因就在下一句 
		b>>=1;//b右移一位,相当于除以2,反正b是整形,如果b是奇数,直接除2和减一再除2的效果是一样的 
		a = a*a;//b除2之后当然a要自己乘自己 
	} 
	return ans;
}

代码:

#include<iostream>
using namespace std;
#define int long long
const int p = 100000007;
int ans[25005];
int fpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1)
			ans = (ans*a)%p;
		b>>=1;
		a = (a*a)%p;
	} 
	return ans%p;
}
signed main()
{
	int a;
	ans[1] = 1;
	for(int i=2;i<=25000;i++)
	{
		ans[i] = (ans[i-1] + fpow(i,i))%p;
	}
	while(cin>>a)//不能用while(!cin.eof())
	{
		cout<<ans[a]+1<<'\n';
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

owooooow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值