1.当a,b,c 都比较小的时候,可以使用赤裸裸的暴力
伪代码:
v:=1;
for i := 1 to b begin
v:=v*a;
v:=v mod c;
end
这也是我们在第一次遇到这种问题的时候首先能想到的.
2.a,b,c都比较大的时候
这里需要考虑c的大小了,假设c*c<2^64吧(再大就只能再做特殊处理了)
b比较大的话使用1的方法显然时间上是承受不了的,所以可以利用所谓的二分法.
b=b0+b1*2^1+b2*2^2+...+bn*2^n
显然a^b可以变成若干项的乘积
伪代码:
v:=1;
while b<>0 do begin
if (b and 1= 1) do begin v:=v*a;v:=v mod c;end
a:=a*a;
a:=a mod c;
b:=b shr 1;
end
3.如果c很大,那么需要使用到模拟乘法来避免溢出,当然如果c已经大到10^100,那么只能用高精了..这里只能处理最多c=2^64-1的情况吧
伪代码:
int mul(int a,int b,int c)
{
int ret=0,tmp=a%c;
while(b)
{
if(b&0x1)if((ret+=tmp)>=c)ret-=c;
if((tmp<<=1)>=c)tmp-=c;
b>>=1;
}
return ret;
}
4.如果b已经爆大,那么使用logb的算法显然已经力不从心
于是可以考虑利用 循环节 的方法来求解
如果c比较小,那么可以直接暴力得到循环节
对于a^b mod c
a.if gcd(a,c)==1
那么可以知道它的循环节开始位置必然是1,而且循环节长既为它的欧拉函数的因子
因为有a^p mod c= 1
所以p可以看成是周期
b.if gcd(a,c)!=1 那么循环节是否一定不从1开始呢?答案是否定的
why?
因为假设存在某个L'使
a mod c = a^(1+L') mod c= a* a^L' mod c
那么可以解到许多满足条件的B',使a*B' = a mod c (mod c)
解的个数就是gcd(a,c),然后如果能找到a^L' = B' (mod c)
那么其开始位置依然可以是1
当然其他大多数情况下循环节开始位置都不是1
好了下面用暴力得到了循环节开始位置spos,和循环节长len
下面用到 公式
就可以在比较快的时间内求解了
如果c非常大那怎么办?
可以利用抽屉原理+baby-step,giant-step扩展应该可以得到循环节的长度,接下来循环节的开始位置我还没有想到比较好的算法
什么是循环节?
那么满足条件的最小的b称为循环节开始位置
满足条件的最小的正整数为T称为循环节长度(T>0)
首先通过下面的程序可以很容易的验证
1..106的数,他的循环节不超过106
#include<iostream>
using namespace std;
const int C=107;
int hash[1000001];
int main()
{
int n,sp,len,i,tmp;
for(n=1;n<=106;n++)
{
memset(hash,0,sizeof(hash));
hash[n%C]=1;
tmp=n;
for(i=2;;i++)
{
tmp*=n;
tmp%=C;
if(hash[tmp%C])break;
}
cout<<n<<':'<<hash[tmp%C]<<' '<<i-hash[tmp%C]<<endl;
}
return 0;
}
并且循环节的开始位置都是1。
当然这个只是对于比较特殊的数如107才满足开始位置为1,如果对于任意的数,那么循环节的开始位置不一定是1。
下面讨论一般情况
假设a^b mod c的循环节是len,开始位置是spos
定义一个操作op()取得k
0.op(b)
1.if b<spos then k=b
2. else k=spos+(b-spos)%len
最后计算a^k mod c既可
这样可以极大的简化问题,使得b的规模 大幅度减小
下面介绍下如何求a^(n!) mod c
有了上面的方法,我相信你们都想到了吧,就是求a^op(n!) mod c
下面开始讨论
op(n!)
if n!<spos k=n!//通常spos很小
else k=spos+(n!-spos)%len= spos+((n!)%len-spos%len+len)%len
问题又转化为求n!%len
那么
1.n>=len n!%len=0
2.n<len
当然了过程中不要忘记取mod
当C比较大的时候怎么求循环节?下面给出一个方法
1.令W=C//想想为什么?
2.解模方程
求得最大的b,b为开始位置,w-b为循环节长度
可是当w-b非常大,n却<w-b的时候,复杂度变成了o(n)...若n过大可能导致超时.
下面的问题就转化成能否快速计算n! % c 类型的问题了
伪代码:
v:=1;
for i := 1 to b begin
v:=v*a;
v:=v mod c;
end
这也是我们在第一次遇到这种问题的时候首先能想到的.
2.a,b,c都比较大的时候
这里需要考虑c的大小了,假设c*c<2^64吧(再大就只能再做特殊处理了)
b比较大的话使用1的方法显然时间上是承受不了的,所以可以利用所谓的二分法.
b=b0+b1*2^1+b2*2^2+...+bn*2^n
显然a^b可以变成若干项的乘积
伪代码:
v:=1;
while b<>0 do begin
if (b and 1= 1) do begin v:=v*a;v:=v mod c;end
a:=a*a;
a:=a mod c;
b:=b shr 1;
end
3.如果c很大,那么需要使用到模拟乘法来避免溢出,当然如果c已经大到10^100,那么只能用高精了..这里只能处理最多c=2^64-1的情况吧
伪代码:
int mul(int a,int b,int c)
{
int ret=0,tmp=a%c;
while(b)
{
if(b&0x1)if((ret+=tmp)>=c)ret-=c;
if((tmp<<=1)>=c)tmp-=c;
b>>=1;
}
return ret;
}
4.如果b已经爆大,那么使用logb的算法显然已经力不从心
于是可以考虑利用 循环节 的方法来求解
如果c比较小,那么可以直接暴力得到循环节
对于a^b mod c
a.if gcd(a,c)==1
那么可以知道它的循环节开始位置必然是1,而且循环节长既为它的欧拉函数的因子
因为有a^p mod c= 1
所以p可以看成是周期
b.if gcd(a,c)!=1 那么循环节是否一定不从1开始呢?答案是否定的
why?
因为假设存在某个L'使
a mod c = a^(1+L') mod c= a* a^L' mod c
那么可以解到许多满足条件的B',使a*B' = a mod c (mod c)
解的个数就是gcd(a,c),然后如果能找到a^L' = B' (mod c)
那么其开始位置依然可以是1
当然其他大多数情况下循环节开始位置都不是1
好了下面用暴力得到了循环节开始位置spos,和循环节长len
下面用到 公式
就可以在比较快的时间内求解了
如果c非常大那怎么办?
可以利用抽屉原理+baby-step,giant-step扩展应该可以得到循环节的长度,接下来循环节的开始位置我还没有想到比较好的算法
什么是循环节?
![http://hiphotos.baidu.com/aekdycoin/pic/item/fa5414230e23f0854623e8ab.jpeg](https://i-blog.csdnimg.cn/blog_migrate/82e41680bf2f1841808d476b3deb958e.jpeg)
那么满足条件的最小的b称为循环节开始位置
满足条件的最小的正整数为T称为循环节长度(T>0)
首先通过下面的程序可以很容易的验证
1..106的数,他的循环节不超过106
#include<iostream>
using namespace std;
const int C=107;
int hash[1000001];
int main()
{
int n,sp,len,i,tmp;
for(n=1;n<=106;n++)
{
memset(hash,0,sizeof(hash));
hash[n%C]=1;
tmp=n;
for(i=2;;i++)
{
tmp*=n;
tmp%=C;
if(hash[tmp%C])break;
}
cout<<n<<':'<<hash[tmp%C]<<' '<<i-hash[tmp%C]<<endl;
}
return 0;
}
并且循环节的开始位置都是1。
当然这个只是对于比较特殊的数如107才满足开始位置为1,如果对于任意的数,那么循环节的开始位置不一定是1。
下面讨论一般情况
假设a^b mod c的循环节是len,开始位置是spos
定义一个操作op()取得k
0.op(b)
1.if b<spos then k=b
2. else k=spos+(b-spos)%len
最后计算a^k mod c既可
这样可以极大的简化问题,使得b的规模 大幅度减小
下面介绍下如何求a^(n!) mod c
有了上面的方法,我相信你们都想到了吧,就是求a^op(n!) mod c
下面开始讨论
op(n!)
if n!<spos k=n!//通常spos很小
else k=spos+(n!-spos)%len= spos+((n!)%len-spos%len+len)%len
问题又转化为求n!%len
那么
1.n>=len n!%len=0
2.n<len
![http://hiphotos.baidu.com/aekdycoin/pic/item/6b5977f5c6b27d4fddc474a9.jpeg](https://i-blog.csdnimg.cn/blog_migrate/253c4b5baf4e1e5f22977c7f60f9974a.jpeg)
当然了过程中不要忘记取mod
当C比较大的时候怎么求循环节?下面给出一个方法
1.令W=C//想想为什么?
2.解模方程
![http://hiphotos.baidu.com/aekdycoin/pic/item/e79df4b06c7f9975092302b6.jpeg](https://i-blog.csdnimg.cn/blog_migrate/cbd0233298fd6147ee7d1927a95e90f4.jpeg)
求得最大的b,b为开始位置,w-b为循环节长度
可是当w-b非常大,n却<w-b的时候,复杂度变成了o(n)...若n过大可能导致超时.
下面的问题就转化成能否快速计算n! % c 类型的问题了