中国剩余定理(CRT)是用来求这样的一个不定方程组的解的:
⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧x≡a1(modb1)x≡a2(modb2)x≡a3(modb3)……x≡an(modbn)
其中 bi 两两互质。
我们可以先考虑一个简单点的问题:
⎩⎪⎨⎪⎧x≡2(mod3)x≡3(mod5)x≡2(mod7)
我们可以:
-
令 y1 除以 3 余 2,除以 5 余 0,除以 7 余 0
-
令 y2 除以 3 余 0,除以 5 余 3,除以 7 余 0
-
令 y3 除以 3 余 0,除以 5 余 0,除以 7 余 2
那么这个方程组的一个解就是 x=y1+y2+y3。
继续对 y1 分解(其实对 y2 和 y3 也同理),令 z 除以 3 余 1,除以 5 余 0,除以 7 余 0,那么显然有 y1=z∗2。
令 M=i=1∏nbi=105,那么 z 肯定能被 b1M=3105=35 整除,不妨令 z=35k。那么 35k≡1(modb1) 即 35k≡1(mod3)。很明显,k 就是 35 在模 3 意义下的逆元。
所以 y1=a1⋅b1M⋅inv(b1M,b1) 即 y1=2∗35∗2=140。
归纳一下,yi=ai⋅biM⋅inv(biM,bi),那么 x=i=1∑nai⋅biM⋅inv(biM,bi)。
但由于这时候求出的 x 只是任意解,最小解 xmin 即为 xmodlcm(b1,b2,b3,…,bn)。
所以方程组
⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧x≡a1(modb1)x≡a2(modb2)x≡a3(modb3)……x≡an(modbn)
的最小解为:
xmin=i=1∑nai⋅biM⋅inv(biM,bi)modlcm(b1,b2,b3,…,bn)
其中 M=i=1∏nbi。
可以发现 biM 其实能用前缀和维护。
模板题代码:
#include <iostream>
#include <cstdio>
using namespace std;
int n;
long long a[15],b[15];
long long sum[2][15];
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(a<b)
{
return exgcd(b,a,y,x);
}
if(b==0)
{
x=1;
y=0;
return a;
}
long long tmpx,tmpy;
long long res=exgcd(b,a%b,tmpx,tmpy);
x=tmpy;
y=tmpx-a/b*tmpy;
return res;
}
inline long long inv(long long a,long long p) // 扩欧求逆元
{
long long x,y;
if(exgcd(a,p,x,y)!=1)
{
return -1;
}
return (x%p+p)%p;
}
inline long long lcm(long long a,long long b)
{
long long tmpa=a,tmpb=b;
long long t=a%b;
while(t!=0)
{
a=b;
b=t;
t=a%b;
}
return tmpa*tmpb/b;
}
int main()
{
scanf("%d",&n);
long long llcm=1;
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&b[i],&a[i]);
llcm=lcm(llcm,b[i]);
}
// 前缀和维护 M/b_i
sum[0][0]=sum[1][n+1]=1;
for(int i=1;i<=n;i++)
{
sum[0][i]=sum[0][i-1]*b[i];
}
for(int i=n;i>=1;i--)
{
sum[1][i]=sum[1][i+1]*b[i];
}
long long ans=0;
for(int i=1;i<=n;i++)
{
ans+=a[i]*sum[0][i-1]*sum[1][i+1]*inv(sum[0][i-1]*sum[1][i+1],b[i]);
}
printf("%lld\n",ans%llcm); // 求最小解
return 0;
}