[TJOI2009]猜数字

题目描述

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

输入输出格式

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

输出所求的整数n。


  也就是求出n,让n满足bi|n-ai。我们将式子转化一下,bi|n-ai => n-aiΞ0(mod bi) => nΞai(mod bi),也就是一个同余方程了。如果只解决这一个,我们可以直接一个扩欧敲下去,但这里有k个方程。我们再看题,题目要求我们求出最小的n满足所有同余方程,并且b1,b2...bn两两互质。这不就是中国剩余定理吗?所以把板子打上去就可以了。这题要注意的是ai可能为负数,不过我们把它转成正的就可以了。还有就是直接乘会爆long long,所以我们还要用到喜闻乐见的快速乘。

#include <cstdio>
#define maxn 15
using namespace std;

long long a[maxn], b[maxn], m[maxn], t[maxn];

inline long long read(){
    register long long x(0), f=1; register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9')
        x=(x<<1)+(x<<3)+(c^48), c=getchar();
    return x*f;
}

inline long long mul(long long a, long long b, long long c){
    long long ans=0;
    while(b){
        if(b&1) ans=(ans+a)%c;
        a=(a<<1)%c;
        b>>=1;
    }
    return ans;
}

void ex_gcd(long long a, long long b, long long &x, long long &y){
    if(!b) x=1, y=0;
    else{
        long long x1, y1;
        ex_gcd(b, a%b, x1, y1);
        x=y1, y=x1-a/b*y1;
    }
}

int main(){
    long long n=read();
    for(register int i=1; i<=n; i++) a[i]=read();
    for(register int i=1; i<=n; i++) b[i]=read();
    for(register int i=1; i<=n; i++) a[i]=(a[i]%b[i]+b[i])%b[i];
    long long tot=1, tmp;
    for(register int i=1; i<=n; i++) tot*=b[i];
    for(register int i=1; i<=n; i++) m[i]=tot/b[i];
    for(register int i=1; i<=n; i++) ex_gcd(m[i], b[i], t[i], tmp),t[i]=(t[i]%b[i]+b[i])%b[i];
    long long ans=0;
    for(register int i=1; i<=n; i++) ans=(ans+mul(mul(a[i],m[i],tot),t[i],tot))%tot;
    ans=(ans+tot)%tot;
    printf("%lld\n", ans);
    return 0;
} 

  刚学懂中国剩余定理的可以来肝这个裸题。

原文地址:https://www.cnblogs.com/akura/p/10729241.html