快速幂
快速幂充分利用了二进制的特点(非0即1),把十进制转成二进制表达后再展开,ans对于每一位的当前结果要么乘要么不乘,把原本长度是p的循环变成了长度是p二进制位数(log2(n) )的循环。
举例说明:
已知这样一个事实:
有了它,就可以由前一位二进制幂结果推出后一位二进制幂结果,对于每一位二进制幂结果由0和1决定是否乘于ans(0也可以看做给ans乘上了1)。
如此,长度是11的循环变成了长度是4的循环。
对于p很大的情况,此算法的”快“会充分的体现。
而快速幂取模的算法则因为 的成立,加上取模就能使用
部分转自:https://blog.csdn.net/thearcticocean/article/details/50375115
代码:
typedef long long LL;
LL power(int a,int p){
LL ans=1,temp=a;
while(p){
if(p&1) ans=ans*temp;
temp=temp*temp;
p>>=1;
}
return ans;
}
快速乘
简单的讲:求解a*b%c 其中0<a,b,c<2^64
分析:这样的式子和a^b%c很像,所以可以用类似于快速幂取模的方法来做。即,将b写成二进制来看,然后拆开相加(正因为二进制的特殊性,才有快速乘和快速幂的成功,有关快速幂取模可以参考:http://blog.csdn.net/thearcticocean/article/details/50375115):
32+16+4=52 (实际操作过程中,每次相加都取模)
这一过程和快速幂取模非常相似。
代码:
LL work(){
LL ans=0;
a=a%c;
b=b%c;
while(b>0){
if(b&1) ans=(ans+a)%c;
a=(a+a)%c;
b>>=1;
}
return ans;
}
例题:
思路:
A+B=C+D
假设A+B=C+D=T
C=(2*A)%T
D=(2*B)%T
k次操作就是
C=(2^k*A)%T
D=(2^k*B)%T
A=min(C,D)
代码如下:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll k=1389472389742429877;
ll a=482333897982347239;
ll b=557432748293424892;
ll t=a+b;
ll mmul(ll a,ll b,ll p){//快速乘(是为了不爆long long 比如long long 乘long long 模P 用快速乘的话就涉及一个long long +long long 这样不会爆始模p后始终都在p以内)
ll ans=0;
ll temp=a;
while(b>0){
if(b&1)ans=(ans+temp)%p;
temp=(temp+temp)%p;
b>>=1;
}
return ans%p;
}
ll poww(ll a,ll b,ll p){//快速幂(第一他这里的ans和快速乘不一样,他这里为1,因为他要不断地乘,而快速乘要不断地加,第二,把他这里的乘都换成加就是快速乘了)
ll temp=a;
ll ans=1LL;
while(b>0){
if(b&1)ans=mmul(ans,temp,p);
temp=mmul(temp,temp,p);
b>>=1;
}
return ans;
}
int main(){
ll l=poww(2,k,t);
ll c=mmul(l,a,t)%t;
ll d=mmul(l,b,t)%t;
printf("%lld\n",min(c,d));
}