基础数论(未更新完全)
质数相关算法
01 Miller–Rabin
前置定理:
费马小定理
若 \(p\) 为素数,\(a\)为整数,且\(a\)与\(p\)互素,那么
我们设 \(d\times 2^r=p-1\) ,那么式子就变成了
二次探测定理
若 \(p\) 是奇素数,\(a<p\),则 \(a^2\equiv 1(\mod p)\) 的解为
或
作者不会证
然后我们把这两个式子放到一起,最佳搭配!
那么对于下面的两个式子,必满足其一
\(Miller–Rabin\)过程
将 \(p-1\) 中的因子为 \(2\) 的个数提取出来,设 \(d=p-1\) ,取 \(k\) 个数 \(1<a_1,a_2,a_3…a_k<p\) 带入我们融合出来的公式判断是否构成。
时间复杂度\(O(k\log p)\)。
\(code:\)
//#pragma GCC optimize("O2")
#include<bits/stdc++.h>
using namespace std;
long long P[7]={2,3,5,13,17,31,37};
inline long stimes(long long a,long long b,long long mod){
long long sum=0;
while(b){
if(b&1){
sum=(sum+a)%mod;
}
a=(a+a)%mod;
b>>=1;
}
return sum;
}
long long qpow(long long a,long long b,long long mod){
long long sum=1;
while(b){
if(b&1){
sum=stimes(sum,a,mod)%mod;
}
a=stimes(a,a,mod)%mod;
b>>=1;
}
return sum;
}
long long Miller_Rabin(long long x,long long p){
long long d=x-1,r=0;
while(!(d&1)){
r++;
d>>=1;
}
long long sum=qpow(p,d,x);
if(sum==1){
return 1;
}
for(int i=0;i<r;i++){
if(sum==x-1){
return 1;
}
sum=stimes(sum,sum,x);
}
return 0;
}
inline long is_p(long long x){
if(x<=1){
return 0;
}
for(int i=0;i<7;i++){
if(x==P[i]){
return 1;
}
if(x%P[i]==0){
return 0;
}
if(!Miller_Rabin(x,P[i])){
return 0;
}
}
return 1;
}
int main(){
int T;
cin>>T;
while(T--){
long long x;
cin>>x;
if(is_p(x)){
puts("YES");
}else{
puts("NO");
}
}
return 0;
}
BSGS,原根和阶
01 阶
定义
首先,阶长这样\(\delta _m(a)\),
读作\(a\) %\(m\)意义下的阶。
我们让\(\delta _m(a)=x\),
\(x\)是最小满足
的数。
一些性质
(1)
我们发现:
注意力惊人
(2)
即任意数的阶都是\(\varphi\)的因数
(3)
不证了,作者不会
02 原根
定义
有一个数\(n\),
\(g^0,g^1,g^2…g^{\varphi(n)-1}\),不相等,
那么\(g\)为\(\mod n\)的原根。
注:
\(\varphi(n)\)表示的是\(\le n\)的数中和\(n\)互质的数的个数。
\(eg:\)
当\(n=5\)时,
\(2\)为$ \mod n$的一个原根,
因为
互不相等。
一些性质
(1)
同时3也是$ \mod 5$的一个原根,
所以5有两个原根。
据此,我们发现:
又来
如果\(p\)是一个质数,那么\(p\)的原根数为\(\varphi(\varphi(p))\)。
(2)
只有形如:
的数有原根。
03 BSGS算法
即大步小步算法,常用于求解离散对数问题,说白了就是在\(O(\sqrt p)\)的时间复杂度下解决\(a^x\equiv b(\mod p)\)中\(x\)的值的算法,要求\(a\)和\(p\)互质。
做法:
首先令\(x=A\left \lceil \sqrt p \right \rceil -B\),其中\(0\le A,B\le \left \lceil \sqrt p \right \rceil\),
把它带入方程中得\(a^{A\left \lceil \sqrt p \right \rceil -B}\equiv b(\mod p)\),
进行指数分解和移项后得\(a^{A\left \lceil \sqrt p \right \rceil} \equiv ba^B(\mod p)\),
然后我们就可以枚举 \(B\) 的值来计算方程右端的所有取值,并把它们存到哈希表中。
接着枚举 \(A\) 来得到方程左端所有值,找到与右端相同时的 \(A\) ,最后就能运用 \(x=A\left \lceil \sqrt p \right \rceil -B\) 来求出方程的解了。
因为\(0\le A,B\le \left \lceil \sqrt p \right \rceil\) ,所以枚举\(A\)和\(B\)的时间复杂度就是\(O(\sqrt p)\) 。
\(code\):
long long BSGS(long long a,long long b,long long p){
map<long long,long long>mp;
long long sum=1,sqrtp=sqrt(p)+1;
for(int B=1;B<=sqrtp;B++){
sum=sum*a%p;//a^b的值
mp[b*sum%p]=B;//存入哈希表
}
long long now=sum;
//sum现在为a^sqrt(p)的值。
for(int A=1;A<=sqrtp;A++){
if(mp[now]){
return (long long)A*sqrtp-mp[now];
}
now=now*sum%p;
}
return -1;
}
04 例题
01 P3846
P3846 [TJOI2007] 可爱的质数/【模板】BSGS
题意即板子,套板子即可。
\(code:\)
//#pragma GCC optimize("O2")
#include<bits/stdc++.h>
using namespace std;
long long BSGS(long long a,long long b,long long p){
map<long long,long long>mp;
long long sum=1,sqrtp=sqrt(p)+1;
for(int B=1;B<=sqrtp;B++){
sum=sum*a%p;//a^b的值
mp[b*sum%p]=B;//存入哈希表
}
long long now=sum;
//sum现在为a^sqrt(p)的值。
for(int A=1;A<=sqrtp;A++){
if(mp[now]){
return (long long)A*sqrtp-mp[now];
}
now=now*sum%p;
}
return -1;
}
int main(){
long long a,b,p;
cin>>p>>a>>b;
int t=BSGS(a,b,p);
if(t==-1){
cout<<"no solution";
}else{
cout<<t;
}
return 0;
}
莫比乌斯反演
01 狄利克雷卷积
定义:
由\(OI\) \(Wiki\)可知:
对于两个数论函数\(f(x)\)和\(g(x)\),它们狄利克雷卷积得到的结果\(h(x)\)定义为:
上述式子简记为:
性质:
交换律:\(f*g=g*f\)
结合律:\((f*g)*h=f*(g*h)\)
分配率:\((f+g)*h=f*h+g*h\)
02 数论分块
引入:
给定一个\(n\),求:
当数据范围较大时,\(O(n)\)会超时,那么可以使用数论分块解决。
数论分块可以快速计算一些含有除法向下取整的和式。
时间复杂度:
\(\left \lfloor \frac{n}{i} \right \rfloor\)的值的集合大小最多为$2\sqrt{n} $。
证明如下
对于正整数 \(i\) ,当 $i\le \sqrt{n} $ 时:
\(i\)最多有\(\sqrt{n}\) 种取值,所以\(\left \lfloor \frac{n}{i} \right \rfloor\)最多只有\(\sqrt{n}\)种取值。
当\(i>\sqrt{n}\)时:
因为\(i>n\)时,\(\left \lfloor \frac{n}{i} \right \rfloor\)的值始终为\(0\),而在\(\sqrt{n}<i\le n\)时,\(i\)最多只有\(\sqrt{n}\)种取值,所以\(\left \lfloor \frac{n}{i} \right \rfloor\)最多也只有\(\sqrt{n}\)种取值。
综上所述,\(\left \lfloor \frac{n}{i} \right \rfloor\)最多有\(2\sqrt{n}\)种取值。
证毕。
所以数论分块的时间复杂度为\(O(\sqrt{n})\)。
左右边界:
在\(\sum_{i=1}^{n} \left \lfloor \frac{n}{i} \right \rfloor\)中,设该块的左边界为\(l\),该块的值为\(\left \lfloor \frac{n}{i} \right \rfloor\),那么该块的右边界\(r\)为$\left \lfloor \frac{n}{ \left \lfloor \frac{n}{i} \right \rfloor}\right \rfloor $
证明如下
还没写
\(code\):
int fk(int n){
int sum=0;
int l=1,r;
while(l<=n){
r=n/(n/l);
sum+=n/l*(r-l+1);
l=r+1;
}
return sum;
}
03 一个符号
04 莫比乌斯反演
引入:
来自\(OI\) \(Wiki\):
OI Wiki我的神!!!
莫比乌斯反演是数论中的重要内容。对于一些函数 \(f(n)\),如果很难直接求出它的值,而容易求出其倍数和或约数和 \(g(n)\),那么可以通过莫比乌斯反演简化运算,求得 \(f(n)\) 的值。
莫比乌斯函数:
运用线性筛求莫比乌斯函数:
//mu存储莫比乌斯函数,smu存储前缀和,vis标记非质数
void get_mu(){
mu[1]=1;
for(int i=2;i<=N;i++){
if(!vis[i]){
mu[i]=-1;
P.push_back(i);
}
for(int p:P){
int j=i*p;
if(j>N){
break;
}
vis[j]=1;
if(i%p==0){
mu[j]=0;
}else{
mu[j]=-mu[i];
}
}
}
for(int i=1;i<=N;i++){
smu[i]=smu[i-1]+mu[i];
}
}
莫比乌斯函数的性质:
即\(\sum_{d\mid n}\mu (d)=[n=1]=\varepsilon (n)\),\(\mu*1=\varepsilon\)
\(\varepsilon(n)\) 的意思
\(\varepsilon(n)=\begin{cases} 1\qquad 如果n=1\\ 0\qquad 如果n>1\end{cases}\)
反演公式:
即:
也就是
还有一个公式是:
反演结论:
05 例题
01 p3455
题意其实就是求
把 \(k\) 消掉,得
然后带入莫反,得
我们让 \(i\) 和 \(j\) 除 \(d\) , 然后我们发现就不用考虑 \(d\) 是不是 \(\gcd(i,j)\) 的因数了,然后我们枚举 \(d\) ,式子就变成了
然后我们注意到里面的两层 \(\sum\) 和 \(\mu(d)\) 没有关系了,把它们提出来
然后我们就可以把后面的 \(\sum\) 消掉
然后我们用线性筛把 \(\mu\) 筛出来
最后我们进行数论分块即可。
\(code:\)
//#pragma GCC optimize("O2")
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=50005;
int n;
int mu[100010],vis[100010],smu[100010];
vector<int> P;
void get_mu(){
mu[1]=1;
vis[0]=vis[1]=1;
for(int i=2;i<=N;i++){
if(!vis[i]){
mu[i]=-1;
P.push_back(i);
}
for(int p:P){
int j=i*p;
if(j>N){
break;
}
vis[j]=1;
if(i%p==0){
mu[j]=0;
break;
}else{
mu[j]=-mu[i];
}
}
}
for(int i=1;i<=N;i++){
smu[i]=smu[i-1]+mu[i];
}
}
int fk(int x,int y,int a){
int ans=0;
x/=a,y/=a;
if(x>y){
swap(x,y);
}
int l=1,r;
while(l<=x){
r=min(x/(x/l),y/(y/l));
ans+=(x/l)*(y/l)*(smu[r]-smu[l-1]);
l=r+1;
}
return ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
get_mu();
int T;
cin>>T;
while(T--){
int a,b,k;
cin>>a>>b>>k;
cout<<fk(a,b,k)<<"\n";
}
return 0;
}