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

题目链接

题意
给定 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],xmi(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 1ai2311,
0 ≤ m i < a i 0≤m_i<a_i 0mi<ai
1 ≤ n ≤ 25 1≤n≤25 1n25
输入样例:
2
8 7
11 9
输出样例:
31

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

x ≡ m 1 x≡m_1 xm1% a 1 a_1 a1
x ≡ m 2 x≡m_2 xm2% a 2 a_2 a2
转化一下
x = m 1 + k 1 ∗ a 1 x=m_1+k_1*a_1 x=m1+k1a1
x = m 2 + k 2 ∗ a 2 x=m_2+k_2*a_2 x=m2+k2a2
消掉 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+k1a1=m2+k2a2
移项得:
k 1 ∗ a 1 − k 2 ∗ a 2 = m 1 − m 2 k_1*a_1 - k_2*a_2=m_1-m_2 k1a1k2a2=m1m2
同时除以 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) k11a1+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=(m1m2)/d
同时:
k 1 = k 1 1 ∗ y k_1=k_1^1*y k1=k11y
k 2 = k 2 1 ∗ y k_2=k_2^1*y k2=k21y
因为我们要找最小正整数,所以我们可以:

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 k1a1+k2(a2)=m1m2

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(a1a2)/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(a1a2)/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+k1a1
得:
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+k1a1+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=k1a1+m1+klcm(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=k1a1+m1
所以就化成了  x = a 0 ∗ k + m 0 x=a_0*k+m_0 x=a0k+m0
我们进行从二到多的推广
就是把 x = a 0 ∗ k + m 0 x=a_0*k+m_0 x=a0k+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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值