洛谷P3868 [TJOI2009]猜数字【中国剩余定理】

题目描述

现有两组数字,每组k个,第一组中的数字分别为:a1,a2,…,ak表示,第二组中的数字分别用b1,b2,…,bk表示。其中第二组中的数字是两两互素的。求最小的非负整数n,满足对于任意的i,n - ai能被bi整除。

输入格式:

输入数据的第一行是一个整数k,(1 ≤ k ≤ 10)。接下来有两行,第一行是:a1,a2,…,ak,第二行是b1,b2,…,bk

输出格式:

输出所求的整数n。

说明

所有数据中,第一组数字的绝对值不超过 1 0 9 10^{9} 109(可能为负数),第二组数字均为不超过6000的正整数,且第二组里所有数的乘积不超过 1 0 18 10^{18} 1018


题目分析

由题意知
{ b 1 ∣ ( n − a 1 ) b 2 ∣ ( n − a 2 ) . . . b k ∣ ( n − a k ) \begin{cases} b_1\mid (n-a_1) \quad \\ b_2 \mid (n-a_2) \quad \\ ...\quad \\ b_k\mid (n-a_k)\quad \\ \end{cases} b1(na1)b2(na2)...bk(nak)
变换成同余方程组得
{ n − a 1 ≡ 0 ( m o d    b 1 ) n − a 2 ≡ 0 ( m o d    b 2 ) . . . n − a k ≡ 0 ( m o d    b k ) \begin{cases} n-a_1\equiv 0(\mod b_1)\quad \\ n-a_2\equiv 0(\mod b_2) \quad \\ ...\quad \\ n-a_k\equiv 0(\mod b_k)\quad \\ \end{cases} na10(modb1)na20(modb2)...nak0(modbk)
根据同余式变换法则
如果有 a ≡ b ( m o d    m ) a\equiv b(\mod m) ab(modm)
a + c ≡ b + c ( m o d    m ) a+c\equiv b+c(\mod m) a+cb+c(modm)成立
将上述方程组变形得
{ n ≡ a 1 ( m o d    b 1 ) n ≡ a 2 ( m o d    b 2 ) . . . n ≡ a k ( m o d    b k ) \begin{cases} n\equiv a_1(\mod b_1)\quad \\ n\equiv a_2(\mod b_2)\quad \\ ...\quad \\ n\equiv a_k(\mod b_k)\quad \\ \end{cases} na1(modb1)na2(modb2)...nak(modbk)

到这里就是裸得中国剩余定理了
不过要注意计算前要将所有 a [ i ] = ( a [ i ] m o d    b [ i ] + b [ i ] ) m o d    b [ i ] ; a[i]=(a[i]\mod b[i]+b[i])\mod b[i]; a[i]=(a[i]modb[i]+b[i])modb[i];

另外这题出题人用(sang)心(xin)良(bing)苦(kuang)
要用快速乘,不然最后一个点爆long long


#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long lt;

lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

int k;
lt a[20],b[20];

lt qmul(lt a,lt b,lt mod)
{
    lt ans=0;
    while(b>0)
    {
        if(b&1) ans=(ans+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return ans;
}

void exgcd(lt a,lt b,lt &x,lt &y)
{
    if(b==0){ x=1; y=0; return;}
    exgcd(b,a%b,x,y);
    int tp=x;
    x=y; y=tp-a/b*y;
}

lt china()
{
    lt ans=0,lcm=1,x,y;
    for(int i=1;i<=k;++i) lcm*=b[i];
    for(int i=1;i<=k;++i)
    {
        lt tp=lcm/b[i];
        exgcd(tp,b[i],x,y);
        x=(x%b[i]+b[i])%b[i];
        ans=(ans+qmul(qmul(tp,x,lcm),a[i],lcm))%lcm;
    }
    return (ans+lcm)%lcm;
}

int main()
{
    k=read();
    for(int i=1;i<=k;++i) a[i]=read();
    for(int i=1;i<=k;++i) b[i]=read();
    for(int i=1;i<=k;i++) a[i]=(a[i]%b[i]+b[i])%b[i];
    cout<<china();
    return 0;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值