本文已参与「新人创作礼」活动,一起开启掘金创作之路。
下述理论主要参考书目: 电子版pdf:算法竞赛进阶指南(p133-150)
@[TOC]
首先补充两个前置知识:约数和最大约数有关性质,可选择性看~
有关素数筛的算法 埃式&欧拉素数筛&大区间素数筛
约数
最大公约数
1.gcd(a,b)=gcd(a,a-b)=gcd(b,a-b)=d;
2.欧几里得算法: b!=0; gcd(a,b)=gcd(b,a%b)=d;
(ps:其实这里有个一般性的结论:gcd(m,)=gcd(m,),n=,k为整数,0<=<m)
1.证明:d|a 且 d|b 那么d| (a-b) 得证
2.证明:a<b 显然易得 a>=b: 令 a=+r; 由于d|a d| 那么易得 d|(a-) 即得d|r 又r=a mod b ,所以gcd(a,b)=gcd(b,a%mod);
欧拉函数
φ(N)=N*(1 - )(1 - )(1 - )......(1 - )
证明:
具体用到了容斥原理: φ(N)=N - ( + + + - - - - - - + + ...-...+..-..) (ps:分式这小东西麻烦啦啊aaa...) 上面的式子因式分解就得到了:
即是: φ(N)=N*(1 - )(1 - )(1 - )......(1 - )
欧拉函数的一些性质
2.性质2我们知道了欧拉函数是积性函数(PS:积性函数:对于任意互质的整数a和b有性质f(ab)=f(a)f(b)的数论函数)
3.p 为质数,若p|n 且 |n,则φ(n)=;
4.p 为质数,若p|n 且 不n整除,则φ(n)=;
5.
证明:
1.由于gcd(n,x)=gcd(n,n-x)=1,所以与n互质的数是"成对"的出现。进而n+(n-x)=n. 就得出了 2.性质2就很显然了,根据欧拉函数的公式 φ(N). a,b互质,所以分解质因数没有一样的,根据欧拉函数就可以将 φ(a) φ(b)合并得出了φ(N)。 所以欧拉函数是积性函数 3.用到了 φ(N)=N (1 - )(1 - )(1 -)......(1 - ) |n主要告诉咱们N分解后p的质因数个数不少于2. 所以分解后的质因数还有p. φ()和φ()的欧拉函数表达式中(1 - )全都一样,只是一个是N和。 所以就得出了φ(N)=; 4.是同理3的证明方法。 5.性质5: 证明比较难了,首先得证明是积性函数. 积性函数证明:其实有点显然了, 若n,m互质,出发点仍然是质因数分解考虑. 举个栗子:n=6, m=35, n,m互质. n中的d:1,2,3. m中的d:1,5,7. 然后我们知道欧拉函数 φ(N)是个积性函数(见性质2)。 的分解得出集合d全是能整除,不多不少。 ps: 得到的d集合(1,5,7,2,10,14,3,15,21). 所以就知道了嘛: n,m互质 那么: 得证:是积性函数
ps:φ(1)=1
同余
定义:若整数a和整数b除以正整数m的余数相等,则称a,b模m同余。记为a b(mod m)。 eg:10 24(mod 7) 同余数3.
同余的基本性质
在写性质之前,先得有种这样的思路。
a=+
b=+
如果ab(mod m),那么=。
性质一: 如果ab(mod m),cd(mod m).那么()() (mod m)。
(ps:从 和这方面思考)。
性质二:如果ab(mod m),cd(mod m).那么()() (mod m)。
证明: 首先从已知得到a和b的余数是,c和d的余数是.
a=+, c=+
然后呢= + +.
对于同理的,那些( +)能被整除的不用管它,然后后面的尾巴都是.所以得以证明。
性质三: 如果acbc(mod m), 且 gcd(c,m)=1(c和m互质) , 则有ab(mod m)。
这条性质很重要,反复品味。
证明: 对于acbc(mod m)有ac=+,bc=+。
所以ac-=bc-.
得到:=。
再可得到中能被m整除。
然后根据算术基本定理:n=p_{1}^{c_1}$$p_{2}^{c_2}$$p_{3}^{c_3}$$p_{4}^{c_4}......
由于gcd(c,m)=1, m不在c中,那么m只能在中。(即是=)
用符号表示:m|(a-b).
a=+
b=+
a-b=+
能被m整除,所以=
这就说明了a和b的余数都是。 所以ab(mod m)。
性质四:若ac≡bd,c≡d(mod m),且gcd(c,m)=1,得到a≡b(mod m)。
(ps:其实性质三是性质四的特殊情况,c=d ) 证明: 首先c与d的余数相等,记为, 又因为gcd(c,m)=1; 所以=1 所以我们得出了gcd(d,m)=1. a=+ c=+ b=+ ac=() + () + () + bd=.........+ 由于ac与bd余数相等,所以。 所以a≡b(mod m)。
同余类与剩余系
gcd(,m)=1; gcd(,m)=gcd(%m,m)=1. (ps:见最大公约数性质二) 所以%m 与 m互质。 所以得出了简化剩余系关于模m乘法封闭。 (ps:这个关系怎么理解呢?对于所有的a,gcd(a,m)=1,那么gcd(a%m,m)=1,即是所有a都能映射到m的简化剩余系中)
费马小定理 & 欧拉定理
1.费马小定理:若p是质数,则对于任意整数a,有(mod p)。 2.欧拉定理:若正整数a,n互质,则(mod n). ps:我个人一般会写成(mod p). 证明: 设n的简化剩余系为{,,,\cdots$$\overline{a_{φ(n)}}}。对于,若(mod n)。 根据同余性质三的结论(如果acbc(mod m), 且 gcd(c,m)=1(c和m互质) , 则有ab(mod m)。) 可知道 (mod n)又因为,所以。 这个证明有什么用呢?意味着若,则%n和%n不等。 那么等一系列是一一对应的。 因此,集合{,,,\cdots$$\overline{a_{φ(n)}}}与集合{,,,\cdots$$\overline{aa_{φ(n)}}}都能表示n的简化剩余系。 所以我们得出了这样一个东东.... (mod n) +,+,+,然后像这样所有的的余数组成的集合就是n的简化剩余系(ps:用到了上面证明的两个集合一一对应的性质,我相信您能看到这里对同余的理解应该很清楚了)就得出了上面的等式。 结合两个等式(mod n) 与n互质(根据同余性质三) 得出了(mod n) 在上述中我们的前提是a与n是互质的。故我们证明了欧拉定理。 如果n是质数,那么φ(n)=n-1。 在欧拉定理的基础上左右乘以a,得到(mod n) 即证明了费马小定理
趁热打铁来一道关于BSBG的算法题(依据费马下定理)
Discrete Logging POJ - 2417
问题: 给定求最小非负整数x,满足(mod ) p是质数。 令 , 所以(mod p) 左右边同乘以,得到:(mod p) 然后就可以枚举i和j了。 首先从枚举j,将得到的b的值存入hash值。 然后,从枚举,计算,查表,如果有值与之相等,则得到的是最小值. 然后最小的非负数整数x若存在一定在1-p 区间内。 注意事项:
1.为什么最小的非负数整数x若存在一定在 区间内 因为费马小定理:(mod p) 进而得出(mod p) x=k*(p-1)+r. 1<r<p-1 所以:(mod ) 这说明了什么呢,一个大于等于p的x,总能在r ,找出r。 2.为什么m要取就可以? 因为是枚举嘛,开根号的时间复杂度是多少的。 3.一点j点可能会被覆盖呀??对没错。枚举j时出来的值是有可能重复,那么在map就会再次刷新, 然后若是刷新啦,j刷新值会比原来值更大(ps:在代码中解释会更直观)。j值越大,得到的x越小(),也是我们希望的。 4.关于枚举i,j区间的问题: 为什么从0−m枚举j,而从1−m枚举i? i不能为0,否则im−j有可能出现负数的情况. hmmm 其实枚举i,j的区间并不是一定的。 依据: 还能枚举有: , 参考资料:BSGS算法 学习笔记
#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
//const int N=2e5+10;
const ll mod=1e9+7;
using namespace std;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
ll a,b,p;
ll qpow(ll x,ll y){
ll ans=1;
while(y){
if(y&1){
ans=(ans*x)%p;
}
x=(x*x)%p;
y>>=1;
}
return ans;
}
ll x;
map<ll,int> mp;
void solve(){
// cout<<qpow(2,10)<<endl;
while(scanf("%lld %lld %lld",&p,&a,&b)!=EOF){
a%=p;
if(a==0 and b==0) {
printf("%d\n",1);
return ;
}
if(a==0)
{
printf("no solution\n");
return ;
}
//注意一个小细节,就是在快速幂中是p哦,别写成了mod啦,一个小小的坑~
ll m=ceil(sqrt(p));
mp.clear();
ll ans=b%p;
//枚举区间i:1-m
for(int i=1;i<=m;i++){
ans=ans*a%p;
mp[ans]=i;
// cout<<ans<<" "<<(i+1)<<endl;
}
ans=1;
int flag=0;
ll t=qpow(a,m);
// ll top=p/m+1;
for(int i=1;i<=m;i++){
ans=ans*t%p;
if(mp[ans]){
// cout<<(i*m-mp[ans]+p)%p<<endl;
printf("%lld\n",(i*m-mp[ans]+p)%p);
flag=1;
break;
}
}
if(!flag) printf("no solution\n");
// cout<<"no solution"<<endl;
}
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
solve();
return 0;
}
欧拉定理的推论
最小正整数
欧拉的指数循环节
其他理论
1. i在区间[x,g(x)]内, 的值都相等。
说明:
求出i的一段连续区间使得: 是个定值。 eg: k=33,i=10; =3; 但是呢? =3; 满足 =3; 满足 =3; 满足 =2; 不满足 所以他的一段连续的区间是:[8,11]。 i在[8,11]内, 是个定值。
证明:
由于g(x)>=x,所以i在这个区间内是个定值。
来看一到题:余数之和
题意很简单:
这里就用上了该结论。
大概讲一下写代码的思路。
我们知道一个N的正因数个数不超过2* sqrt(N).
(ps:a<b =N 那么a<sqrt(n) 和 b>sqrt(n) 所以显而易见)
知道了这个就可以说明了 i在[1,N]内 值不同的个数不超过 .
然后就知道了有不超过2* sqrt(N)个的区间。时间复杂度在O();
AC代码:
#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll mod=1e9+7;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
ll n,k;
ll sum=0;
void solve(){
n=read();k=read();
sum=n*k;
ll x=1;
ll y;
ll cnt;
while(x<=n){
if(k/x !=0)
y=k/(k/x);
else y=n;
cnt=y-x+1;
if(y<=n)
sum-= (k/x)*cnt*(x+y)/2;
else {
sum-=(k/x)*(n-x+1)*(x+n)/2;
}
x=y+1;
}
cout<< sum<<endl;
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
solve();
// while(1);
return 0;
}
参考博客: 同余 Discrete Logging BSGS算法