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;
}
但实际上孙子定理还没有完……还得继续仔细看数论讲义