TJOI2009 猜数字

https://www.luogu.org/problemnew/show/P3868

题目

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

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

题解

孙子定理的练手题……

套公式,解是

\[x\equiv \sum_{i=i}^n M_iM'_ia_i\pmod{\prod_{i=1}^nb_i}\]

其中

\[M_i=\frac{\prod_{j=1}^nb_j}{b_i},M'_iM_i\equiv1\pmod{b_i}\]

那么直接求和就好了,还需要注意爆long long(通过qmul解决),用qmul需要注意乘号右边不能小于0,否则是未定义行为……(G++直接死循环)

求逆的时候需要注意不能直接用费马小定理,因为b不一定是素数,$\varphi(b)\ne{b-1}$!

所以只能用EXGCD,但是还是要注意得出来的结果并不保证大于0,所以放右边要注意换为大于0的数字(就算放左边a又要小于0,需要处理a了……)

AC代码

#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<cstring>
#include<algorithm>
#include<set>
#define REP(r,x,y) for(register int r=(x); r<(y); r++)
#define REPE(r,x,y) for(register int r=(x); r<=(y); r++)
#define PERE(r,x,y) for(register int r=(x); r>=(y); r--)
#ifdef sahdsg
#define DBG(...) printf(__VA_ARGS__),fflush(stdout)
#else
#define DBG(...) (void)0
#endif
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
char ch; int si;

#define gc() getchar()

template<class T>
inline void read(T &x) {
    x=0; si=1; for(ch=gc();!isdigit(ch) && ch!='-';ch=gc());
    if(ch=='-'){si=-1,ch=gc();} for(;isdigit(ch);ch=gc())x=x*10+ch-'0';
    x*=si;
}
template<class T, class...A> inline void read(T &x, A&...a){read(x); read(a...);}

inline LL qmul(LL a, LL b, LL p) {
    LL ans=0;
    for(;b;b>>=1) {
        if(b&1) ans=(ans+a)%p;
        a=(a*2)%p;
    }
    return ans;
}

inline void exgcd(LL a, LL b, LL &x, LL &y) {
    if(b==0) {x=1, y=0; return;}
    exgcd(b,a%b,y,x);
    y-=a/b*x;
    //return d;
}


#define MAXN 17
LL a[MAXN],b[MAXN],M[MAXN],M_[MAXN];
LL B=1;
int main() {
#ifdef sahdsg
    freopen("in.txt","r",stdin);
#endif
    int k; read(k);

    REP(i,0,k) {read(a[i]);}
    REP(i,0,k) {read(b[i]); B*=b[i];}
    LL ans=0;
    REP(i,0,k) {
        //B/b[i] === 1 mod b[i]
        //B/b[i]x+b[i]*y=1
        LL x,t;
        exgcd(B/b[i],b[i],x,t);
        x%=B; if(x<0) x+=B;
        ans=(ans+qmul(B/b[i]*a[i],x,B))%B;
    }
    if(ans<0) ans+=B;
    printf("%lld\n", ans);
    return 0;
}

 但实际上孙子定理还没有完……还得继续仔细看数论讲义

猜你喜欢

转载自www.cnblogs.com/sahdsg/p/10816765.html