前言:
如何評價 OI 逐漸 MO 化。
我數學不是很好,最近學了這玩意,寫一篇總結。
$\text{Part I 素數篩} $
-
埃式篩 Eraosthenes
- 大概思路:從2開始,由小到大掃描每一個數 \(x\),把他的倍數標記為合數。當掃描到一個數時,若尚未被標記,那么就是質數。
- 復雜度:\(O(n \log \log n)\) 接近線性。
- 代碼:
void make_prime()
{
memset(prime,true,sizeof(prime));
prime[0]=prime[1]=false;//0,1都不是素數
int t=sqrt(n);//開根號
for(int i=2;i<=t;i++)
{
if(prime[i])
{
for(int j=2*i;j<=n;j+=i)//加倍
{
prime[j]=false;
}
}
}
return;
}
-
線性篩
- 大致思路:從小到大積累質因子
- 復雜度:\(O(n)\)
void init() {
phi[1] = 1;
for (int i = 2; i < MAXN; ++i) {
if (!vis[i]) {
phi[i] = i - 1;
pri[cnt++] = i;
}
for (int j = 0; j < cnt; ++j) {
if (1ll * i * pri[j] >= MAXN) break;
vis[i * pri[j]] = 1;
if (i % pri[j]) {
phi[i * pri[j]] = phi[i] * (pri[j] - 1);
} else {
// i % pri[j] == 0
// 換言之,i 之前被 pri[j] 篩過了
// 由於 pri 里面質數是從小到大的,所以 i 乘上其他的質數的結果一定會被
// pri[j] 的倍數篩掉,就不需要在這里先篩一次,所以這里直接 break
// 掉就好了
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
}
}
}
$\text{Part II Prime Numbers and Phi} $
\(\phi(n)\) 表示 \(1~n\) 中與 \(n\) 互質的個數。同時也是歐拉函數。
然后有很多好玩的性質,由於我 \(\LaTeX\) 實在不大行就先不寫了。
代碼:
void euler(int n)
{
for(int i=2;i<=n;i++) ph[i]=i;
for(int i=2;i<=n;i++)
{
if(ph[i]==i)
{
for(int j=i;j<=n;j+=i)
{
ph[j]=ph[j]/i*(i-1);
}
}
}
}
$\text{Part III 歐幾里得} $
- \(\gcd\):歐幾里得算法,俗稱輾轉相除。
int gcd(int a,int b)
{
return b ? gcd(b,a%b) : a;
}
- 擴展歐幾里得:對於任意整數 \(a,b\) ,存在一對整數 \(x,y\) ,滿足 \(ax+by=\gcd(a,b)\) 。不過說句閑話,這個應該裴蜀定理。解這個方程算法被稱之為“擴展歐幾里得”。
int exgcd(int a,int b,int &x,int &y)
{
if(a==0)
{
x=0;y=1;
return b;
}
int d=exgcd(b%a,a,y,x);
x-=b/a*y;
return d;
}
提一嘴逆元:若整數 \(b,m\) 互質,且 \(b|a\),則存在一個整數 \(x\),使得 \(\frac{a}{b}\) 和 \(a\times x\) 模 \(m\) 同余。則稱 \(x\) 為 \(b\) 的模 \(m\) 乘法逆元。然后這玩意一般都是用來轉換除法的。
$\text{Part IIII 同余方程} $
對於像 ACwing203 這種題直接用歐幾里得算法求出一種特解,然后就最小值即可。
中國剩余定理:
直接放代碼:
/*long long gcd(LL a,LL b)
{
return b==0?a:gcd(b,a%b);
}*/
#include<cstdio>
#define ll long long
//擴展歐幾里得算法
void gcd(ll a,ll b,ll &d,ll &x,ll &y)
{
if(b==0){
d=a;
x=1,y=0;
}
else{//else不能省略
gcd(b,a%b,d,y,x);
y-=(a/b)*x;
}
}
//中國剩余定理
ll China(int n,ll *m,ll *a)
{
ll M=1,d,y,x=0;
for(int i=0;i<n;i++) M*=m[i];
for(int i=0;i<n;i++){
ll w=M/m[i];
gcd(m[i],w,d,d,y);
x=(x+y*w*a[i])%M;
}
return (x+M)%M;
}
ll m[15],a[15];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lld%lld",&m[i],&a[i]);
printf("%lld",China(n,m,a));
}