这是今天做的一道快速幂的题,看完题目之后用笔在草稿纸上写写就能分析出来其中的数学公式 ans=(Σpow(2,i)*(pow(2,k*len)-1)/(pow(2,len)-1))%M 但是代码交上去之后一直WA,总是不明白哪里错了,WA了快十次实在不知道怎么改了,于是上网搜了下题解,发现了新大陆!!
在刚才写的公式里面有一个除法求模的运算,之前的代码我就直接相除取模了
ans=(ans+foo(2,i)*((Pkl-1)/(Pl-1)))%M;
实则是不可取的,因为(a/b)%M 并不会等于(a%M)/(b%M)%M
在我看的题解的分析中有说到关于求除法模运算的方法,但是简单带过了,只知道是叫 乘法逆元 于是我就搜索乘法逆元啊,找到了两篇博客:
一篇是数学证明:http://www.cnblogs.com/tiankonguse/archive/2012/08/14/2638949.html
另一篇是求乘法逆元的方法:http://sujinyue.is-programmer.com/posts/38179.html
(这是博客原文,地址在上面,博主若是不希望被转载通知立删)
看了几遍之后大概了解了其中的思路,然后自己验证了一下题目中给定的M=1000000007是素数,那么快速幂求pow(pl,M-2)就可以了,方便简洁。这个方法对快速幂的题目算是升华吧,再也不怕除法模了。
下面是题目代码:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<string>
#include<iostream>
using namespace std;
long long M=1000000007;
char s[100];
long long foo(long long a,long long b)
{
// printf("pow(%lld,%lld)=",a,b);
long long ans=1;
a=a%M;
while(b)
{
if(b%2) ans=(ans*a)%M;
b/=2;
a=(a*a)%M;
}
// printf("%lld ",ans);
return ans;
}
int main()
{
// freopen("in.txt","r",stdin);
long long ans;
while(scanf("%s",s)!=EOF)
{
long long len;
len=strlen(s);
if(s[0]=='-')
{
for(int i=1;i<len;++i)
s[i-1]=s[i];
--len;
}
long long k;
scanf("%lld",&k);
long long Pl;
Pl=foo(2,len);
long long Pkl;
Pkl=foo(Pl,k);
ans=0;
for(long long i=0;i<len;++i)
{
if(s[i]=='0'||s[i]=='5')
{
ans=(ans+foo(2,i)*((Pkl-1)/(Pl-1)))%M;
}
}
printf("%lld\n",ans);
}
return 0;
}