同余定理
(a + b) % m = (a % m + b % m) % m
(a * b) % m = ((a % m) * (b % m)) % m
在前面的快速幂中用到。以及在高精度取模中用到。
高精度取模即把高精度数看成各位数的权值与个位数乘积的和。
比如1234 = ((1 * 10 + 2) * 10 + 3) * 10 + 4,对这个数进行取余运算就是上面基本加和乘的应用。
代码如下
#include
#include
#include
using namespace std;
int main(){
string a;//把数用字符存起来
int b;
cin >> a >> b;
int len = a.length();
int ans = 0;
for(int i = 0; i < len; i++){
ans = (ans * 10 + a[i] - '0') % b;//同余定理
}
printf("%d\n",ans);
return 0;
}
逆元
对于整数a,p,若存在一个整数x,使ax≡1 (mod p)即ax%p=1成立,则称x是a对p的逆元(当且仅当gcd(a,p)=1时,才存在且存在唯一逆元),我们用inv(a)表示x
应用:求式子(a/b)%m的时候,由于b过大会出现爆精度的现象,有时为了精度也会用b的逆元去求式子。
b的逆元可以看作b在这个式子里的又一个倒数,b的逆元B会满足原式=(a×B)%m,但b并不是数学意义上的b的倒数,b的倒数往往是一个小数,而b的逆元不会这样。但是在这里b的逆元又满足这个式子。
费马小定理:若p是质数且gcd(a,p)=1,则a^(p-1)≡1 (mod p)
显然我们有a*a^(p-2)≡1 (mod p),即inv(a)=a^(p-2),用快速幂计算inv(a),复杂度log2(a),当模数p是质数时,用费马求解
·欧拉定理:若gcd(a,p)=1,则a^(φ(p))≡1(mod p)
显然此时inv(a)=a^(φ(p)-1),使用欧拉函数+快速幂计算,复杂度同费马,但较费马而言,模数p可以不为质数,因此应用范围广一点
·扩展欧几里得:若gcd(a,b)=1,则ax+by=1
//欧几里德
ll extend_gcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
else {
ll r = extend_gcd(b, a % b, y, x);
y -= x * (a / b);
return r;
}
}
ll inv(ll a, ll n) {
ll x, y;
extend_gcd(a, n, x, y);
x = (x % n + n) % n;
return x;
}
51nod1256 乘法逆元
给出2个数M和N(M < N),且M与N互质,找出一个数K满足0 < K < N且K * M % N = 1,如果有多个满足条件的,输出最小的。
解:真——求乘法逆元。。代码如下
#include
#include
#include
#include
long long m,x,y;
using namespace std;
void ggcd(int a,int b){
if(b==0){
x=1;
y=0;
return;
}
else{
ggcd(b,a%b);
long long temp=x;
x=y;//相当于x1=y2
y=temp-(a/b)*y;//相当于y1=x2-(a/b)*y2
}
}
int main(){
long long n,h;
scanf("%lld %lld",&n,&m);
ggcd(n,m);
while(x<0){
x=(x+m)%m;
}
printf("%lld\n",x);
return 0;
}
2. 51nod1013 3的幂的和
求:3^0 + 3^1 +...+ 3^(N) mod 1000000007
解:Sn=a1*(1-q^(n+1))/(1-q);q=3,a1=1,化简(q^(n+1)-1)/2;即这个式子对1000000007去余。
先求出2对1000000007的逆元,然后快速幂求q^(n+1)(因为(q^(n+1)太大了,所以在求的过程中一直对1000000007取余,防止爆精度),再-1,乘以2的逆元,最后对1000000007取余;代码如下:
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int mod=1000000007;
long long X,y;
long long gcd(long long a,long long b){//计算逆元
int t,d;
if(b==0){
X=1;
y=0;
return a;
}
d=gcd(b,a%b);
t=X;
X=y;//相当于x1=y2
y=t-(a/b)*y;//相当于y1=x2-(a/b)*y2
return d;
}
ll ksm(long long x,long long a,long long p){//快速幂取余
ll ans=1;
x=x%p;
while(a>0){
if(a&1){
ans=(ans*x)%p;
}
x=(x*x)%p;
a>>=1;
}
return ans;
}
int main(){
long long n;
scanf("%lld",&n);
gcd(2,mod);//2%m 原式化简得(q^(n+1)-1)/ 2%m, 所以求2对mod的逆元;
if(X<0){//
X+=mod;
}
n++;
ll ans=(ksm(3,n,mod)-1)*X%mod;//所以原式就变成了(q^(n+1)-1)×x(逆元)%m;
printf("%lld\n",ans);
return 0;
}
算数基本定理
任何一个大于1的自然数N,如果N不为质数,那么N可以分解成有限个质数的乘积,并且在不计次序的情况下,这种分解方式是唯一的。
容斥定理
要计算几个集合并集的大小,我们要先将所有单个集合的大小计算出来,然后减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分,依此类推,一直计算到所有集合相交的部分。