快速幂
快速幂就是快速算底数的n次幂。其时间复杂度为 O(log₂N), 与朴素的O(N)相比效率有了极大的提高。
-
原理
一个数b ( b > 0 ) 一定可以表示为若干指数不重复的2的次幂的和
(k为b在二进制下表示的位数个数)
如 b = C k − 1 2 k − 1 + C k − 2 2 k − 2 + . . . + C 0 2 0 b=C_{k-1}2^{k-1}+C_{k-2}2^{k-2}+...+C_{0}2^{0} b=Ck−12k−1+Ck−22k−2+...+C020
于是ab可以表示为
a b = a C k − 1 2 k − 1 × a C k − 1 2 k − 1 × . . . × a C 0 2 0 a^{b}=a^{C_{k-1}2^{k-1}} \times a^{C_{k-1}2^{k-1}}\times...\times a^{C_{0}2^{0}} ab=aCk−12k−1×aCk−12k−1×...×aC020公式 r e s = ∏ i = 0 k − 1 a C i 2 i res=\prod_{i=0}^{k-1}a^{C_{i}2^{i}} res=∏i=0k−1aCi2i
快速幂模板(求ab)
#include<iostream>
using namespace std;
typedef long long ll;
int main()
{
int a,b,p,res=1;
cin>>a>>b>>p;
for(;b;b>>=1)
{
if(b&1) res=(ll)res*a%p; //防止爆int,开long long
a=(ll)a*a%p;
}
cout<<res%p; //防止p=1,b=0时,所以再mod p
}
约数
这里主要讲一下一个数的约数之和和约数个数
根据算术基本定理,正整数N可以被唯一分解为质数的次幂的乘积的形式
N = p 1 c 1 p 2 c 2 . . . p m c m ( p 为 质 数 ) N=p_{1}^{c_{1}}p_{2}^{c_{2}}...p_{m}^{c_{m}} (p为质数) N=p1c1p2c2...pmcm(p为质数)
所以N的约数个数
( c 1 + 1 ) × ( c 2 + 1 ) × . . . × ( c m + 1 ) = ∏ i = 1 m ( c i + 1 ) (c_{1}+1)\times(c_{2}+1)\times...\times(c_{m}+1)=\prod_{i=1}^{m}(c_{i}+1) (c1+1)×(c2+1)×...×(cm+1)=∏i=1m(ci+1)
约数之和为
( 1 + p 1 + p 1 2 + . . . + p 1 c 1 ) × . . . × ( 1 + p m + p m 2 + . . . + p m c m ) = ∏ i = 1 m ∑ j = 0 c i p i j (1+p_{1}+p_{1}^{2}+...+p_{1}^{c_{1}})\times...\times(1+p_{m}+p_{m}^{2}+...+p_{m}^{c_{m}})=\prod_{i=1}^{m}\sum_{j=0}^{c_{i}}p_{i}^{j} (1+p1+p12+...+p1c1)×...×(1+pm+pm2+...+pmcm)=∏i=1m∑j=0cipij
(希望大家自己推一下,不浪费多少时间)
这里我们就来一道有点难度的题
约数之和
求 A B A^{B} AB的所有约数之和
在套用公式的时候需要注意的是公式的变形
然后用等比数列求解(用乘法逆元将除法变为乘法,原理为费马小定理)
(思考一下)
代码仅供参考,也有别的解法
#include<iostream>
#include<algorithm>
#include<unordered_map>
#define mod 9901
using namespace std;
typedef long long ll;
unordered_map<int,int>tt;
int a,b;
ll qmi(int a,ll b){
//快速幂
ll res=1;
for(;b;b>>=1){
if(b&1) res=(ll)res*a%mod;
a=(ll)a*a%mod;
}
return res;
}
int main(){
cin>>a>>b;
if(a==0){
cout<<0;
return 0;
}
for(int i=2;i<=a/i;i++){
//分解质因子
while(a%i==0){
a/=i;
tt[i]++;
}
}
if(a>1) tt[a]++;
ll res=1;
for(auto i:tt){
int x=i.first,y=i.second;
if((x-1)%mod==0) //没有逆元的情况
res=((ll)b*y+1)%mod*res%mod;
else
res=(ll)res*(qmi(x,(ll)y*b+1)-1)%mod*qmi(x-1,(ll)mod-2)%mod;//等比数列公式
res=(res+mod)%mod;//防止负数
}
cout<<res;
return 0;
}
扩展欧几里得算法
对于任意整数a和b,存在一对整数x和y,满足 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)
换而言之
公式 a x + b y = d ax+by=d ax+by=d 有x和y的解就必须满足 g c d ( a , b ) ∣ d gcd(a,b)\mid d gcd(a,b)∣d (这个推论非常重要)
结合欧几里得算法就可以解出很多问题了(包括扩展中国剩余定理)
这里给出证明过程(写纸上)
模板题
线性同余方程
代码如下
#include<iostream>
using namespace std;
int n;
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,y,x); //将x和y掉位置,方便一点
y-=a/b*x;
return d;
}
int main(){
cin>>n;
while(n--){
int a,b,m,x,y;
scanf("%d%d%d",&a,&b,&m);
int d=exgcd(a,m,x,y);
if(b%d) puts("impossible");
else printf("%d\n",(long long)b/d*x%m);
}
return 0;
}
本来还想写一下中国剩余定理和高斯消元的,想想算了,这得写两小时啊,哪来这么多时间