中国剩余定理
定义: 中国剩余定理其实就是多个线性同于方程的方程组;举个例子,《孙子算经》中曾提到过这样一个问题: 有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何。 其实这就是一个经典的中国剩余定理的例子。
算法流程:
计算所有模的积n -----> 对于第 i 个方程:计算mi = n/ni -------> 计算mi在模ni的意义下的逆元1/mi -------> 计算ci = mi* (mi^-1)(不要对ni取模)得:
[方程为一解为: ans = sum_{i=1}^{k}a_ic_i (mod n)
]
代码实现:
//式中m[i]为除数必须两两互质才能这么算,否则请看excrt算法
//式中b[i]是除数, a[i]是余数
void exgcd(int a,int b,int &x,int &y)
{
if(b==0){ x=1; y=0; return;}
exgcd(b,a%b,x,y);
int tp=x;
x=y; y=tp-a/b*y;
}
int china()
{
int ans=0,lcm=1,x,y;
for(int i=1;i<=k;++i) lcm*=b[i]; //计算n
for(int i=1;i<=k;++i)
{
int tp=lcm/b[i]; //求m[i]
exgcd(tp,b[i],x,y);
x=(x%b[i]+b[i])%b[i]; //x要为最小非负整数解
ans=(ans+tp*x*a[i])%lcm; //ai*ci ci=tp*x
}
return (ans+lcm)%lcm;
}
扩展中国剩余定理
它与中国剩余定理不同之处在于:中国剩余定理中所有的模一定是两两互素的,二在excrt中:所有的模不一定两两互素。
思路为求解k次exgcd即可:
//a[i]和b[i]分别是余数和除数
LL mul(LL a, LL b, LL mod){
LL ans = 0 % mod;
while(b){
if(b & 1) ans = (ans + a) % mod;
a = (a + a) % mod;
b >>= 1;
}
return ans % mod;
}
LL exgcd(LL a, LL b, LL &x ,LL &y)
{
if(b == 0) {x = 1; y = 0; return a;}
LL gcd = exgcd(b, a % b, x, y);
LL tp = x; x = y; y = tp - a / b * y;
return gcd;
}
LL excrt()
{
LL m = b[1], ans = a[1]; //第1个的答案,然后往下递推
for(int i = 2; i <= n; i++)
{
LL c = (a[i] - ans % b[i] + b[i]) % b[i];
LL gcd = exgcd(m, b[i], x, y);
if(c % gcd != 0) return -1; //无解
x = mul(x, c/gcd, b[i]);
ans += x * m;
m *= b[i] / gcd;
ans = (ans % m + m) % m;
}
return (ans % m + m) % m;
}
洛谷excrt模板题:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int gcd(int a,int b) { return b == 0 ? a : gcd(b, a % b); }
LL n, a[maxn], b[maxn];
LL x, y;
LL mul(LL a, LL b, LL mod){
LL ans = 0 % mod;
while(b){
if(b & 1) ans = (ans + a) % mod;
a = (a + a) % mod;
b >>= 1;
}
return ans % mod;
}
LL exgcd(LL a, LL b, LL &x ,LL &y)
{
if(b == 0) {x = 1; y = 0; return a;}
LL gcd = exgcd(b, a % b, x, y);
LL tp = x; x = y; y = tp - a / b * y;
return gcd;
}
LL excrt()
{
LL m = b[1], ans = a[1]; //第1个的答案,然后往下递推
for(int i = 2; i <= n; i++)
{
LL c = (a[i] - ans % b[i] + b[i]) % b[i];
LL gcd = exgcd(m, b[i], x, y);
if(c % gcd != 0) return -1; //无解
x = mul(x, c/gcd, b[i]);
ans += x * m;
m *= b[i] / gcd;
ans = (ans % m + m) % m;
}
return (ans % m + m) % m;
}
int main()
{
scanf("%lld", &n);
for(int i = 1; i <= n; ++i){
scanf("%lld %lld", &b[i], &a[i]); //除数和余数
}
printf("%lld
", excrt());
system("pause");
}