窝来大致说一下数论的一些东西:
1.欧几里得相关的两个定理:
欧几里得原理:
(a,b)=(b,a mod b)
证明:
设r=a mod b,then a=kb+r
设c为a,b的任意公因数
则:
c|a;c|b;c|(a−kb) <=> c|r
所以c也是b,r的因数
同理,我们设d为b,r的任意因数,则可知d也是a,b的公因数。
设A={d|a and d|b},B={d|r and d|b}, thenA=B
所以我们可以知道(a,b)=(b,r)
证明完毕!
代码如下:
int gcd(int x,int y){return y ? (y,x % y) : x;}
扩展欧几里得算法:
贝祖等式x,y∈Z,则ax+by=(a,b)有解。
证明:
我们可以直接用扩展欧几里得算法步骤来证明:
根据 (a,b)=(b,a mod b)
则设:
ax1+by1=(a,b)
bx2+(a mod b)y2=(b,a mod b)
所以:ax1+by1=bx2+(a mod b)y2
根据取模的定义,我们知道
a mod b=a−⌊ab⌋⋅b
那么可以把等式右边替换:
ax1+by1=bx2+(a−⌊ab⌋⋅b)y2
我们知道,如果这样一直递归下去,那么肯定会使得y的系数为0。
这样子就能证明有解了。
那么怎么计算出方程的值呢?
我们根据递归出口:
when b==0,then x=1,y=0
理由如下:
ax+by=(a,b)=a,则x=1,y=0必然是一组解。
回到刚刚得问题上:
ax1+by1=bx2+(a−⌊ab⌋⋅b)y2
我们现在已经知道了右边等式的系数都是多少,现在怎么知道 x1和y1的值呢?
移项:
a(x1−y2)+b(y1−x2+⌊ab⌋⋅y2)=0
那么显然有:
x1=y2;y1=x2−⌊ab⌋⋅y2
那么我们可以得出算法代码:
void exgcd(int a,int b,int &x,int &y){b ? (exgcd(b,a % b,y,x),y -= a / b * x): (x = 1,y = 0);}
中场休息时间QAQ
中国剩余定理:
内容如下:
假如有n个同余方程:
x≡ai(mod mi),且mi两两互素。
设 M=∏mi
求其在模 M 意义下的解。
我们假设
再设 ti为Mi在模mi下的逆元 ,即 tiMi=1(mod mi)
那么我们就可以方程的解:
x=∑aitiMi
证明:
先证明x合法。
即证明∀i∈[1,n],x≡ai(mod mi)。
x=∑j≠iajtjMj+aitiMi
则在 mod mi的意义下:
x=∑0+ai=ai(mod mi)
这样x是合法的。
实际上,我们可以肯定x是小于M的。
因为如果 x=∑aitimi>M合法,那么显然可以把M减去对吧。
我们这样就得到 模M 意义下的一个解了.
但是……
注意一下实际上我们是用CRT去求解同余方程组的。
所以我们应该没有必要那么麻烦。
我们考虑把同余方程合并:
对于
x≡r1modm1
x≡r2modm2
那么我们想把它合成为1个方程。
k1m1+r1=k2m2+r2
k1m1−k2m2=r2−r1
可以扩欧来求,对吧。
当 d=gcd(m1,m2)/|(r2−r1) 时,无解。
我们想让这个 k1 尽量小,并且是正整数。
那么显然的恒等式是:
k1m1+k2m2+lcm(k1,k2)−lcm(k1,k2)=r2−r1
k1(m1%k2d)+k2(m2+p)=r2−r1
这个 p 显然是补足
所以我们可以注意到,这个 m1 显然可以在模意义下去搞。
这样我们就求出了一个 x ,注意到,
x=r2−r1(mod lcm(m1,m2))
这样一个一个合并就好了。
#include <cstdio>
#include <algorithm>
#include <cstring>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
using namespace std;
typedef long long LL;
LL m[1002],r[1002];
int n;
LL gcd(LL a,LL b){return b ? gcd(b,a % b) : a;}
void exgcd(LL a,LL b,LL &x,LL &y){b ? (exgcd(b,a % b,y,x),y -= a / b * x) : (x = 1,y = 0);}
LL crt()
{
LL R,M,k1,k2;
R = r[1],M = m[1];
for(int i = 2;i <= n;++ i)
{
LL dt = r[i] - R,d = gcd(m[i],M);
if(dt % d)return -1;
exgcd(M / d,m[i] / d,k1,k2);
k1 = (k1 * dt / d) % (m[i] / d);
R = R + k1 * M;
M = M / d * m[i];
R %= M;
}
return R < 0 ? R + M : R;
}
int main()
{
while(~scanf("%d",&n))
{
Rep(i,n)scanf("%lld%lld",&m[i],&r[i]);
printf("%lld\n",crt());
}
return 0;
}