表达整数的奇怪方式(中国剩余定理)

题目链接

题意
给定 2n 个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1​,a2​,…,an​和 m 1 , m 2 , … , m n , m_1,m_2,…,m_n, m1​,m2​,…,mn​,求一个最小的非负整数 x,满足 ∀ i ∈ [ 1 , n ] , x ≡ m i ( m o d a i ) ∀i∈[1,n],x≡m_i(mod a_i) ∀i∈[1,n],x≡mi​(modai​)。

输入格式
第1 行包含整数 n。

第 2…n+1行:每 i+1 行包含两个整数 a i a_i ai​和 m i m_i mi​,数之间用空格隔开。

输出格式
输出最小非负整数 x,如果 x 不存在,则输出 −1。
如果存在 x,则数据保证 x 一定在64位整数范围内。

数据范围
1 ≤ a i ≤ 2 31 − 1 1≤ai≤2^{31}−1 1≤ai≤231−1,
0 ≤ m i < a i 0≤m_i<a_i 0≤mi​<ai​
1 ≤ n ≤ 25 1≤n≤25 1≤n≤25
输入样例:
2
8 7
11 9
输出样例:
31

题解
先看两个式子的也就是:

x ≡ m 1 x≡m_1 x≡m1​% a 1 a_1 a1​
x ≡ m 2 x≡m_2 x≡m2​% a 2 a_2 a2​
转化一下
x = m 1 + k 1 ∗ a 1 x=m_1+k_1*a_1 x=m1​+k1​∗a1​
x = m 2 + k 2 ∗ a 2 x=m_2+k_2*a_2 x=m2​+k2​∗a2​
消掉 x x x等价于:
m 1 + k 1 ∗ a 1 = m 2 + k 2 ∗ a 2 m_1+k_1*a_1=m_2+k_2*a_2 m1​+k1​∗a1​=m2​+k2​∗a2​
移项得:
k 1 ∗ a 1 − k 2 ∗ a 2 = m 1 − m 2 k_1*a_1 - k_2*a_2=m_1-m_2 k1​∗a1​−k2​∗a2​=m1​−m2​
同时除以 y y y得:


k 1 1 ∗ a 1 + k 2 1 ∗ ( − a 2 ) = g c d ( a 1 , − a 2 ) k_1^1*a_1 + k_2^1*(-a_2)=gcd(a_1,-a_2) k11​∗a1​+k21​∗(−a2​)=gcd(a1​,−a2​)
d = g c d ( a 1 , − a 2 ) , y = ( m 1 − m 2 ) / d d=gcd(a_1,-a_2),y=(m_1-m_2)/d d=gcd(a1​,−a2​),y=(m1​−m2​)/d
同时:
k 1 = k 1 1 ∗ y k_1=k_1^1*y k1​=k11​∗y
k 2 = k 2 1 ∗ y k_2=k_2^1*y k2​=k21​∗y
因为我们要找最小正整数,所以我们可以:

k 1 = k 1 + k ∗ ( a 2 / d ) k_1=k_1+k*(a_2/d) k1​=k1​+k∗(a2​/d)
k 2 = k 2 + k ∗ ( a 1 / d ) k_2=k_2+k*(a_1/d) k2​=k2​+k∗(a1​/d)
按比例加减看这个,
k 1 ∗ a 1 + k 2 ∗ ( − a 2 ) = m 1 − m 2 k_1*a_1 + k_2*(-a_2)=m_1-m_2 k1​∗a1​+k2​∗(−a2​)=m1​−m2​

k 1 k_1 k1​上加减 k ∗ ( a 2 / d ) k*(a_2/d) k∗(a2​/d)对结果的影响是加减 k ∗ ( a 1 ∗ a 2 ) / d k*(a_1*a_2)/d k∗(a1​∗a2​)/d 同时 k 2 k_2 k2​上加减 k ∗ ( a 1 / d ) k*(a_1/d) k∗(a1​/d)造成的影响是加减 k ∗ ( a 1 ∗ ( − a 2 ) ) / d = − k ∗ ( a 1 ∗ a 2 ) / d k*(a_1*(-a_2))/d = -k*(a_1*a_2)/d k∗(a1​∗(−a2​))/d=−k∗(a1​∗a2​)/d所以对结果无影响,还能找到最小的正整数解。

然后我们把
k 1 = k 1 + k ∗ ( ( − a 2 ) / d ) k_1=k_1+k*((-a_2)/d) k1​=k1​+k∗((−a2​)/d)
带入原式:
x = m 1 + k 1 ∗ a 1 x=m_1+k_1*a_1 x=m1​+k1​∗a1​
得:
x = m 1 + k 1 ∗ a 1 + k ∗ ( a 1 ∗ ( − a 2 ) ) / d x=m_1+k_1*a_1+k*(a_1*(-a_2))/d x=m1​+k1​∗a1​+k∗(a1​∗(−a2​))/d
( a 1 ∗ ( − a 2 ) ) / d = = l c m ( a 1 , − a 2 ) (a_1*(-a_2))/d==lcm(a_1,-a_2) (a1​∗(−a2​))/d==lcm(a1​,−a2​)
即:
x = k 1 ∗ a 1 + m 1 + k ∗ l c m ( a 1 , - a 2 ) x=k_1*a_1+m_1+k*lcm(a_1,-a_2) x=k1​∗a1​+m1​+k∗lcm(a1​,-a2​)
我们令 a 0 = l c m ( a 1 , − a 2 ) , m 0 = k 1 ∗ a 1 + m 1 a_0=lcm(a_1,-a_2),m_0=k_1*a_1+m_1 a0​=lcm(a1​,−a2​),m0​=k1​∗a1​+m1​
所以就化成了  x = a 0 ∗ k + m 0 x=a_0*k+m_0 x=a0​∗k+m0​
我们进行从二到多的推广
就是把 x = a 0 ∗ k + m 0 x=a_0*k+m_0 x=a0​∗k+m0​当做第一式子,新输入的当做第二个式子,每次更新第一个式子,注意有些有些地方要用正整数所以用abs().

写的时候可能有的地方 ( − a 2 ) (-a_2) (−a2​)放的地方不对,后来改不知道改完没。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=2e5+7;
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){x=1,y=0;return a;}
    int d=exgcd(b,a%b,x,y);
    int z=x;
    x=y;
    y=z-y*(a/b);
    return d;
}
int main(){
    ll n,m1,a1,a2,m2;
    ll x,y;
    cin>>n;
    cin>>a1>>m1;
    for(int i=1;i<n;i++){
        cin>>a2>>m2;
        ll d=exgcd(a1,-a2,x,y);
        if((m2-m1)%d){ cout<<"-1"<<endl;return 0;}
        x=(x*(m2-m1)/d % abs(a2/d) + abs(a2/d))%abs(a2/d);  
        m1=x*a1+m1;
        a1=abs(a1/d*a2);
    }
    cout<<m1<<endl;
    return 0;
}

posted @ 2020-12-01 13:33  `KingZhang`  阅读(133)  评论(0)    收藏  举报