数论基础

1.最大公因数( g c d gcd gcd):
解法:辗转相除法:
原理: g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)
证明:

假设 a > b a>b a>b ,那么 a a a 可以表示成 a = k ∗ b + r a = k*b + r a=kb+r a , b , k , r a,b,k,r abkr 皆为正整数,且 r < b r<b r<b),
r = a % b r=a\%b r=a%b
假设 d d d a , b a,b a,b 的一个公约数,记作 d ∣ a , d ∣ b d|a,d|b da,db
r = a − k ∗ b r = a - k*b r=akb,两边同时除以 d d d,得: r d = a d − k ∗ b d = m \frac{r}{d}=\frac{a}{d}-\frac{k*b}{d}=m dr=dadkb=m
由等式右边可知 m m m 为整数,因此 d ∣ r d|r dr
因此 d d d 也是 b    ,    a % b b\;,\;a\%b b,a%b 的公约数;
假设 d d d b , a % b b,a\% b b,a%b 的公约数, 则 d ∣ b , d ∣ ( a − k ∗ b ) d|b,d|(a-k*b) db,d(akb) 是一个整数,进而 d ∣ a d|a da
因此 d d d 也是 a , b a,b a,b 的公约数;
因此 ( a , b ) (a,b) (a,b) ( b , a % b ) (b,a\%b) (b,a%b) 的公约数是一样的,其最大公约数也必然相等,
得证。

代码:
int _gcd(int x,int y)
{
    return y?_gcd(y,x%y):x;
}

也可以直接利用 algorithm \text{algorithm} algorithm 库中的函数 __gcd(x,y) \text{\_\_gcd(x,y)} __gcd(x,y)

2.最小公倍数( l c m lcm lcm):

两个数 a , b a,b a,b 的最小公倍数:

ans=a/gcd(a,b)*b;//注意先除后乘,否则容易溢出
3.唯一分解定理:
定义:

任意一个大于 0 0 0 的正整数都能被表示成若干个素数的乘积且表示方法是唯一的。
n = p 1 a 1 ∗ p 2 a 2 ∗ . . . ∗ p k a k n=p_1^{a_1}*p_2^{a_2}*...*p_k^{a_k} n=p1a1p2a2...pkak

应用:

(1) g c d gcd gcd a , b a,b a,b 两数所有相同质因子最小指数幂的乘积。
(2) l c m lcm lcm a , b a,b a,b 两数所有的质因子的最大指数幂的乘积。
可以拓展到多个数。
(3)一个数因子个数:
n = ( 1 + a 1 ) ∗ ( 1 + a 2 ) ∗ ( 1 + a 3 ) ∗ . . . ∗ ( 1 + a k ) n=(1+a_1)*(1+a_2)*(1+a_3)*...*(1+a_k) n=(1+a1)(1+a2)(1+a3)...(1+ak)
(4)一个数的所有因子和:
n = ∏ i = 1 k ∑ j = 0 a i p i j = ( 1 + p 1 1 + p 1 2 + . . . + p 1 a 1 ) ∗ ( 1 + p 2 1 + p 2 2 + . . . + p 2 a 2 ) ∗ . . . ( 1 + p k 1 + p k 2 + . . . + p k a k ) n=\prod_{i=1}^{k}{\sum_{j=0}^{a_i}{p_i^{j}}}=(1+p_1^1+p_1^2+...+p_1^{a_1})*(1+p_2^1+p_2^2+...+p_2^{a_2})*...(1+p_k^1+p_k^2+...+p_k^{a_k}) n=i=1kj=0aipij=(1+p11+p12+...+p1a1)(1+p21+p22+...+p2a2)...(1+pk1+pk2+...+pkak)

质因子分解代码:

复杂度: O ( n 1 2 ) O(n^{\frac{1}{2}}) O(n21)

//注意初始化
cnt=0;
for(int i=2;i*i<=num;i++)//sqrt(n)的复杂度
{
    if(num%i==0)
    {
        factor[++cnt]=i;//存质因子
        while(tn%i==0)
        {
            e[cnt]++;//质因子的幂
            num/=i;
        }
   }
}
if(num>1)//不能省略!
{
     factor[++cnt]=num;
     e[cnt]++;
}

还可以预处理出素数表来加快分解速度。
大整数质因子分解:Pollard Rho因数分解

4.素数筛法:
埃氏筛:
实现:

  首先将 2 2 2 n n n 范围内的整数写下来,其中 2 2 2 是最小的素数。将所有的 2 2 2 的倍数筛去,剩下的最小的数字就是 3 3 3 ,它不能被更小的数整除,所以 3 3 3 是素数。再将所有的 3 3 3 的倍数划去……以此类推。如果剩余的最小的数是 m m m,那么 m m m 就是素数。然后将所有 m m m 的倍数划去,像这样反复操作,就能依次枚举 n n n 以内的素数。
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

代码:
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=1e5+5;
vector<int>prime;//存素数
bool vis[N];//vis=false为素数
void Sieve()
{
    memset(vis,0,sizeof(vis));
    vis[1]=1;
    for(int i=2;i<N;i++)
    {
        if(!vis[i])
        {
            prime.pb(i);
            for(int j=i;j<N;j+=i)//建议用加法
            //for(int j=1;j*i<N;j++)
                vis[j]=1;
        }
    }
}
int main()
{
    Sieve();
    for(int i=0;i<10;i++)
        cout<<prime[i]<<endl;
    return 0;
}

不足:一个数可能会被不同的质因子筛多次,造成时间的浪费。

欧拉筛(线性筛):

保证了每个合数只会被它的最小素因子筛掉,就把复杂度降到了 O ( N ) O(N) O(N)

代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
bool vis[N];//标记非素数
vector<int>prime;//保存素数
void euler()
{
    prime.clear();
    memset(vis,0,sizeof(vis));
    for(int i=2;i<N;i++)
    {
        if(!vis[i])
            prime.push_back(i);
        for(int j=0;j<prime.size()&&i*prime[j]<N;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)//重点
                break;
        }
    }
}
int main()
{
    euler();
    return 0;
}
应用:

求积性函数。

5.快速幂:
定义:

要求 a b a^b ab,朴素做法是把 a a a 连乘 b b b 次。时间复杂度为 O ( n ) O(n) O(n)
b b b 拆成二进制,该二进制数第 i i i 位的权为 2 i 2^i 2i
所以 a b = a 2 k + . . . + 2 0 a^b=a^{2^k+...+2^0} ab=a2k+...+20

代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll power(ll a,ll b,ll mod)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res%mod;
}
int main()
{
    cout<<power(1LL*100,1LL*100,1LL*11)<<endl;
    return 0;
}
6.逆元(乘法逆元):
定义:

a ∗ x = 1 ( m o d    p ) a*x=1 (mod \;p) ax=1(modp),则 x x x 称为 a a a 在模 p p p 意义下的逆元,即 x = i n v ( a ) x=inv(a) x=inv(a)
( a + b ) % p = ( a % p + b % p ) % p (a + b) \% p = (a\%p + b\%p) \%p (a+b)%p=(a%p+b%p)%p (√)
( a − b ) % p = ( a % p − b % p ) % p (a - b) \% p = (a\%p - b\%p) \%p (ab)%p=(a%pb%p)%p (√)
( a ∗ b ) % p = ( a % p ∗ b % p ) % p (a * b) \% p = (a\%p * b\%p)\%p (ab)%p=(a%pb%p)%p (√)
( a / b ) % p = ( a % p / b % p ) % p (a / b) \% p = (a\%p / b\%p) \%p (a/b)%p=(a%p/b%p)%p × \times ×

求法:
(1)费马小定理:

p p p 为素数, a a a 为正整数,且 a , p a,p a,p 互质时,有
a ( p − 1 ) = 1 ( m o d    p ) a^{(p-1)}=1 (mod \;p) a(p1)=1(modp)
i n v ( a ) = a p − 2 inv(a)=a^{p-2} inv(a)=ap2
用快速幂取模求解即可。
复杂度: O ( l o g n ) O(logn) O(logn)

(2)拓展欧几里得:

a ∗ x = 1 ( m o d    p ) ⟹ a ∗ x + p ∗ y = 1 a*x=1 (mod\;p) \Longrightarrow a*x+p*y=1 ax=1(modp)ax+py=1
转化和求 x x x 的最小正整数解(转化为 [ 0 , p ) [0,p) [0,p) 内即可)。
要求满足 g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1,否则方程无解,逆元不存在。
复杂度: O ( l o g n ) O(logn) O(logn)

(3)递推求逆元:

[ 1 , p ) [1,p) [1,p) 内的逆元。
复杂度: O ( n ) O(n) O(n)
公式 i n v [ i ] = ( p − p / i ) ∗ i n v [ p % i ] % p inv[i]=(p-p/i)*inv[p\%i]\%p inv[i]=(pp/i)inv[p%i]%p
推导:
p = k ∗ i + r p=k*i+r p=ki+r,有
k ∗ i + r = 0 ( m o d    p ) k*i+r=0 (mod\;p) ki+r=0(modp)
r = − k ∗ i ( m o d    p ) r=-k*i(mod \;p) r=ki(modp)
两边同时除以 r ∗ i r*i ri,有 i n v [ i ] = − k ∗ i n v [ r ] ( m o d    p ) inv[i]=-k*inv[r](mod\;p) inv[i]=kinv[r](modp)
k , r k,r k,r 替换掉, i n v [ i ] = ( − k ) ∗ i n v [ p % i ] % p inv[i]=(-k)*inv[p\%i]\%p inv[i]=(k)inv[p%i]%p
因为 ( − k ) % p = ( p − k ) % p (-k)\%p=(p-k)\%p (k)%p=(pk)%p,所以有 i n v [ i ] = ( p − p / i ) ∗ i n v [ p % i ] % p inv[i]=(p-p/i)*inv[p\%i]\%p inv[i]=(pp/i)inv[p%i]%p
初始化 i n v [ 1 ] = 1 inv[1]=1 inv[1]=1,即可递推求出所有逆元。
补充: [ 1 , p ) [1,p) [1,p) 内的所有数的逆元对于 [ 1 , p ) [1,p) [1,p) 内的所有数。
代码

void get_inv()
{
    inv[1]=1;
    for(int i=2;i<p;i++)
        inv[i]=(p-p/i)*inv[p%i]%p;
}
阶乘逆元:
求解步骤:

可以先求出最后一个数的阶乘( n ! n! n!)的逆元,然后利用一下公式向前推导:
i n v [ i ] = i n v [ i + 1 ] ∗ ( i + 1 ) inv[i]=inv[i+1]*(i+1) inv[i]=inv[i+1](i+1)

公式证明:

1 i ! = 1 ( i + 1 ) ! ∗ ( i + 1 ) \frac{1}{i!}=\frac{1}{(i+1)!}*(i+1) i!1=(i+1)!1(i+1)

7.素数分布定理:
定义:

对正整数 x x x,记 π ( x ) π(x) π(x) 为不大于 x x x 的素数个数。
lim ⁡ x → ∞ π ( x ) x / l n ( x ) = 1 \lim\limits_{x\rightarrow\infty}\frac{ \pi(x)}{x/ln(x)}=1 xlimx/ln(x)π(x)=1

大致分布表:
x x x π ( x ) \pi(x) π(x)
1 0 1 10^1 101 4 4 4
1 0 2 10^2 102 25 25 25
1 0 3 10^3 103 168 168 168
1 0 4 10^4 104 1 , 229 1,229 1,229
1 0 5 10^5 105 9 , 592 9,592 9,592
1 0 6 10^6 106 78 , 498 78,498 78,498
1 0 7 10^7 107 644 , 579 644,579 644,579
1 0 8 10^8 108 5 , 761 , 455 5,761,455 5,761,455
1 0 9 10^9 109 50 , 847 , 534 50,847,534 50,847,534
1 0 10 10^{10} 1010 455 , 052 , 511 455,052,511 455,052,511

例题:2019 ICPC Asia Xuzhou Regional C. < 3 <3 <3 numbers

8.组合数:
组合数公式:

C ( n , m ) = n ∗ ( n − 1 ) ∗ ( n − 2 ) ∗ . . ∗ ( n − m + 1 ) m ! = n ! ( n − m ) ! ∗ m ! (1) C(n,m)=\frac{n*(n-1)*(n-2)*..*(n-m+1)}{m!}=\frac{n!}{(n-m)!*m!} \tag{1} C(n,m)=m!n(n1)(n2)..(nm+1)=(nm)!m!n!(1)

C ( n , m ) = C ( n , n − m ) (2) C(n,m)=C(n,n-m)\tag{2} C(n,m)=C(n,nm)(2)

线性递推:

C ( n , k ) = C ( n , k − 1 ) ∗ ( n − k + 1 ) k C(n,k)=C(n,k-1)*\frac{(n-k+1)}{k} C(n,k)=C(n,k1)k(nk+1)

杨辉三角:

递推式: C ( n , m ) = C ( n − 1 , m ) + C ( n − 1 , m − 1 ) C(n,m)=C(n-1,m)+C(n-1,m-1) C(n,m)=C(n1,m)+C(n1,m1)
复杂度: O ( n 2 ) O(n^2) O(n2)

代码:
void init()
{
    for(int i=0;i<=n;i++)
    {
        C[i][0]=C[i][i]=1;
        for(int j=1;j<i;j++)
            C[i][j]=C[i-1][j-1]+C[i-1][j];
    }
}
二项式定理:
公式:

( x + y ) n = C ( n , 0 ) ∗ x 0 ∗ y n + C ( n , 1 ) ∗ x 1 ∗ y ( n − 1 ) + . . . + C ( n , n ) ∗ x n ∗ y 0 (x+y)^n=C(n,0)*x^0*y^n+C(n,1)*x^1*y^{(n-1)}+...+C(n,n)*x^n*y^0 (x+y)n=C(n,0)x0yn+C(n,1)x1y(n1)+...+C(n,n)xny0

9.位运算:

(1)判断 n n n 是否为 2 2 2 的整数次幂: n & ( n − 1 ) = = 0 n\&(n-1)==0 n&(n1)==0
(2)找到 n n n 中最低的 1 1 1 位:n&(~(n-1));
(3) a + b = a ⨁ b + 2 ∗ ( a & b ) a+b=a\bigoplus b+2*(a\&b) a+b=ab+2(a&b),异或是不进位的加法;
(4)或( ∣ | ) 可以用来判断子集;

10.同余方程:
一元线性同余方程:
欧几里得算法:

g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)

拓展欧几里得:

方程 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),必然有解。
证明:
b = 0 b=0 b=0 时,有 g c d ( a , b ) = a gcd(a,b)=a gcd(a,b)=a { x 0 = 1 y 0 = 0 \begin{cases}x_0=1\\y_0=0\end{cases} {x0=1y0=0   为一组解。

b    ! = 0 b\;!=0 b!=0时,
a ∗ x 1 + b ∗ y 1 = g c d ( a , b ) = b ∗ x 2 + ( a % b ) ∗ y 2 a*x_1+b*y_1=gcd(a,b)=b*x_2+(a\%b)*y_2 ax1+by1=gcd(a,b)=bx2+(a%b)y2
⇒ a ∗ x 1 + b ∗ y 1 = b ∗ x 2 + ( a − ( a / b ) ∗ b ) ∗ y 2 \Rightarrow a*x_1+b*y_1=b*x_2+(a-(a/b)*b)*y_2 ax1+by1=bx2+(a(a/b)b)y2
⇒ a ∗ x 1 + b ∗ y 1 = a ∗ y 2 + b ∗ ( x 2 − ( a / b ) ∗ y 2 ) \Rightarrow a*x_1+b*y_1=a*y_2+b*(x_2-(a/b)*y_2) ax1+by1=ay2+b(x2(a/b)y2)

所以: { x 1 = y 2 y 1 = x 2 − ( a / b ) ∗ y 2 \begin{cases} x_1=y_2\\y_1=x_2-(a/b)*y_2 \end{cases} {x1=y2y1=x2(a/b)y2

据此,一直向下递归,直到到达边界 b = 0 b=0 b=0。然后回溯,利用不同方程的解的关系求解。

三种常见应用:
1.求解不定方程:

对于 a x + b y = c ax+by=c ax+by=c 的不定方程,
{ c % g c d ( a , b ) ! = 0 , 无 整 数 解 c % g c d ( a , b ) = 0 , 有 整 数 解 \begin{cases}c\%gcd(a,b)!=0,无整数解\\c\%gcd(a,b)=0,有整数解 \end{cases} {c%gcd(a,b)!=0,c%gcd(a,b)=0,
c % g c d ( a , b ) = 0 c\%gcd(a,b)=0 c%gcd(a,b)=0 时,
先求解方程 a ∗ x + b ∗ y = g c d ( a , b ) a*x+b*y=gcd(a,b) ax+by=gcd(a,b),然后把解同时乘以 c / g c d ( a , b ) c/gcd(a,b) c/gcd(a,b),即可得出原方程的解。
假设 x 0 , y 0 x_0,y_0 x0,y0 为方程的一组解,
则通解:

{ x = x 0 + b / g c d ( a , b ) ∗ k y = y 0 − a / g c d ( a , b ) ∗ k \begin{cases}x=x_0+b/gcd(a,b)*k\\y=y_0-a/gcd(a,b)*k\end{cases} {x=x0+b/gcd(a,b)ky=y0a/gcd(a,b)k

把通解代入原方程,可以发现恒成立。

x x x 的最小正整数解: x = ( x % b + b ) % b x=(x\%b+b)\%b x=(x%b+b)%b

注意:在一些问题中 a , b a,b a,b 等系数可能为负。

2.求解模线性方程(线性同余方程):

可以转化为求解不定方程。

3.求解模的逆元:

转化为求解不定方程。

拓展欧几里得解不定方程代码:
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll ans=exgcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
    return ans;
}
ll cal(ll a,ll b,ll c)
{
    ll x,y;
    ll gcd=exgcd(a,b,x,y);
    if(c%gcd)
        return -1;
    x=c/gcd*x;
    x=(x%b+b)%b;
    return x;
}
11.中国剩余定理:

一元线性同余方程组:
x = a 1 ( m o d   m 1 ) x=a_1(mod\ m_1) x=a1(mod m1)
x = a 2 ( m o d   m 2 ) x=a_2(mod\ m_2) x=a2(mod m2)
x = a 3 ( m o d   m 3 ) x=a_3(mod\ m_3) x=a3(mod m3)
x = a 4 ( m o d   m 4 ) x=a_4(mod\ m_4) x=a4(mod m4)

x = a n ( m o d   m n ) x=a_n(mod\ m_n) x=an(mod mn)
其中 m 1 , m 2 , . . . , m n m_1,m_2,...,m_n m1,m2,...,mn 两两互质,对于任意的 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an 方程组有解。
可以把解构造成如下形式:
x = p 1 + p 2 + p 3 + . . . + p n x=p_1+p_2+p_3+...+p_n x=p1+p2+p3+...+pn

其中 p 1 % m 1 = a 1   , . . . ,   p n % m n = a n p_1\%m_1=a_1\ ,...,\ p_n\%m_n=a_n p1%m1=a1 ,..., pn%mn=an,且 p i % ∏ j = 1 , j ≠ i n m j = 0 p_i\%\prod_{j=1,j\neq i}^{n}{m_j}=0 pi%j=1,j=inmj=0
那么这样的 x x x 一定满足条件。
以求 p 1 p_1 p1 为例:
假设 t m = ∏ i = 1 , i ≠ 1 n m i tm=\prod_{i=1,i\neq1}^{n}{m_i} tm=i=1,i=1nmi

考虑 t m ∗ t = 1 ( m o d   m 1 ) tm*t=1(mod\ m_1) tmt=1(mod m1)

两边同时乘 a 1 a_1 a1

t m ∗ t ∗ a 1 = a 1 ( m o d   m 1 ) tm*t*a_1=a_1(mod\ m_1) tmta1=a1(mod m1),那么 p 1 = t m ∗ t ∗ a 1 p_1=tm*t*a_1 p1=tmta1 就满足条件了。

因此关键在于求 t t t,可以发现 t t t t m tm tm 的逆元,直接用拓展欧几里得即可求解。

模板代码:(FZU - 1402)
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll m[12],aa[12];
int n;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll ans=exgcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
    return ans;
}
ll crt()
{
    ll M=1,ans=0;
    for(int i=1;i<=n;i++)
        M*=m[i];
    for(int i=1;i<=n;i++)
    {
        ll tm=M/m[i];
        ll x,y;
        ll gcd=exgcd(tm,m[i],x,y);
        ans=(ans+tm*aa[i]*x)%M;
    }
    ans=(ans%M+M)%M;//最小正整数解
    return ans;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%I64d%I64d",&m[i],&aa[i]);
        printf("%I64d\n",crt());
    }
    return 0;
}
拓展CRT:

对于一般情况, m 1 , m 2 , m 3 , . . . , m n m_1,m_2,m_3,...,m_n m1,m2,m3,...,mn 不互质。
主要思路是:两两合并

分析:

首先,对于前两个方程:
{ x = a 1 + m 1 ∗ x 1 x = a 2 + m 2 ∗ x 2 \begin{cases}x=a_1+m_1*x_1\\ x=a_2+m_2*x_2\end{cases} {x=a1+m1x1x=a2+m2x2
合并有:
a 1 + m 1 ∗ x 1 = a 2 + m 2 ∗ x 2 a_1+m_1*x_1=a_2+m_2*x_2 a1+m1x1=a2+m2x2
⇒ m 1 ∗ x 1 + m 2 ∗ x 2 = ( a 2 − a 1 ) \Rightarrow m_1*x_1+m_2*x_2=(a_2-a_1) m1x1+m2x2=(a2a1)
利用拓展欧几里得即可解出该方程的最小的 x 1 x_1 x1 正整数解: x 1 ′ x_1^{'} x1
代入原第一个方程中,就可以求解出满足第一个方程的最小正整数解 x ′ = x 1 ′ ∗ m 1 + a 1 x^{'}=x_1^{'}*m_1+a_1 x=x1m1+a1
且该解同样满足方程 2 2 2,即 x ′ = a 2 + m 2 ∗ x 2 x^{'}=a_2+m_2*x_2 x=a2+m2x2 ,但 x 2 x_2 x2 不必求出。
那么满足所有方程的解 x x x 的形式,一定满足 x = x ′ + k ∗ l c m ( m 1 , m 2 ) x=x^{'}+k*lcm(m_1,m_2) x=x+klcm(m1,m2)
至此,我们就可以把原来的两个方程合并为一个方程:
x = x ′ + l c m ( m 1 , m 2 ) ∗ k x=x^{'}+lcm(m_1,m_2)*k x=x+lcm(m1,m2)k
即把方程的解当作余数, l c m lcm lcm 当作模数,与其他方程形式相同。
以此类推,把所有方程合并,即可求出最后的解。

模板代码(luoguP4777):
#include <bits/stdc++.h>//最小非负整数x
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll m[N],va[N];
ll mul(ll a,ll b,ll mod)
{
    ll res=0;
    while(b>0)
    {
        if(b&1) res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    ll gcd=exgcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
    return gcd;
}
ll cal(ll a,ll b,ll c)
{
    ll x=0,y=0;
    ll gcd=exgcd(a,b,x,y);
    if(c%gcd)
        return -1;
    b/=gcd;
    x=mul(x,c/gcd,b);//注意溢出
    x=(x%b+b)%b;
    return x;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld",&m[i],&va[i]);
    ll r=va[1],t=m[1];//x=r+t*x',为合并后的方程
    for(int i=2;i<=n;i++)
    {
        ll x=cal(t,m[i],(va[i]-r)%m[i]+m[i]);//保证常数c为正,使用快速乘
        //x=x*t+r;
        //r=x;
        r=x*t+r;
        t=m[i]/__gcd(m[i],t)*t;
        r%=t;
    }
    printf("%lld\n",(r%t+t)%t);
    return 0;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值