AcWing 204. 表达整数的奇怪方式 (线性同余方程组)打卡

时间:2021-02-07 14:49:18

给定2n个整数a1,a2,…,ana1,a2,…,an和m1,m2,…,mnm1,m2,…,mn,求一个最小的整数x,满足∀i∈[1,n],x≡mi(mod ai)∀i∈[1,n],x≡mi(mod ai)。

输入格式

第1行包含整数n。

第2..n行:每i+1行包含两个整数aiai和mimi,数之间用空格隔开。

输出格式

输出整数x,如果x不存在,则输出-1。

数据范围

1≤ai≤231−11≤ai≤231−1,
0≤mi<ai0≤mi<ai

输入样例:

2
8 7
11 9

输出样例:31



题意:求出同时满足所有式子要求的最小整数x,如果不存在输出-1
思路:首先没说m互相互质,所以这不是中国剩余定理,这是线性同余方程组问题 这里摘抄一位大佬的

中国剩余定理,又名孙子定理o(*≧▽≦)ツ

能求解什么问题呢?

问题:

一堆物品

3个3个分剩2个

5个5个分剩3个

7个7个分剩2个

问这个物品有多少个

解这题,我们需要构造一个答案

我们需要构造这个答案

5*7*inv(5*7,  3) % 3  =  1

3*7*inv(3*7,  5) % 5  =  1

3*5*inv(3*5,  7) % 7  =  1

这3个式子对不对

显然这里就要用到线性同余方程用扩欧来求解

然后两边同乘你需要的数

2 * 5*7*inv(5*7,  3) % 3  =  2

3 * 3*7*inv(3*7,  5) % 5  =  3

2 * 3*5*inv(3*5,  7) % 7  =  2

a = 2 * 5*7*inv(5*7,  3)

b = 3 * 3*7*inv(3*7,  5)

c = 2 * 3*5*inv(3*5,  7)

那么

a % 3 = 2

b % 5 = 3

c % 7 = 2

其实答案就是a+b+c

因为

a%5 = a%7 = 0 因为a是5的倍数,也是7的倍数

b%3 = b%7 = 0 因为b是3的倍数,也是7的倍数

c%3 = c%5 = 0 因为c是3的倍数,也是5的倍数

所以

(a + b + c) % 3 = (a % 3) + (b % 3) + (c % 3) = 2 + 0 + 0 = 2

(a + b + c) % 5 = (a % 5) + (b % 5) + (c % 5) = 0 + 3 + 0 = 3

(a + b + c) % 7 = (a % 7) + (b % 7) + (c % 7) = 0 + 0 + 2 = 2

你看你看,答案是不是a+b+c(。・ω・)ノ゙,完全满足题意

但是答案,不只一个,有无穷个,每105个就是一个答案(105 = 3 * 5 * 7)

根据计算,答案等于233,233%105 = 23

如果题目问你最小的那个答案,那就是23了

结论:因为我要同时满足多个式子的要求,那么我们来一个一个来满足,当前的余数我就用其他数来拼凑

因为是其他数的乘积,那么其他数mod的话都会等于0,然后只有当前数会有余数,这样就能得出一个其他所有数mod等于0,满足当前数余数要求的数了

然后我们用这个方法给每个数都求一个这样的数,然后求和,那么就能满足所有数的要求了,当然这只限于两两互质情况

不互质的中国剩余定理  -  线性同余方程组

首先为什么要互质呢

因为如果化简成最简质因子的话,如果有相同质因子,那么有可能会mod成0,那么我们也不能借用来获取自己想要的数了

{

x = 2 mod 4

x = 3 mod 6

x = 4 mod 8

}

6*8%4 = 0 ,所以就不行了

这里我们要用线性同余方程组,来两两合并,最后化简成一个来得出答案

AcWing    204. 表达整数的奇怪方式     (线性同余方程组)打卡

然后我们就可以得出推导式

#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
#define LL long long
using namespace std;
LL m[],r[];
void ex_gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
if(b==){ x=;y=;d=a;return ;}
ex_gcd(b,a%b,d,y,x); y-=x*(a/b);
}
LL gcd(LL a,LL b)
{
return b==?a:gcd(b,a%b);
}
LL ex_CRT(int n)
{
LL a,b,c,c1,c2,x,y,d,N;
a=m[]; c1=r[];
for(int i=;i<=n;i++){
b=m[i];c2=r[i]; c=c2-c1;
ex_gcd(a,b,d,x,y);
if(c%d) return -;
LL b1=b/d;//转移式
x=((c/d*x)%b1+b1)%b1;//
c1=a*x+c1; a=a*b1;//
}
/*if(c1==0){
c1=1; for(int i=1;i<=n;i++) c1=c1*m[i]/gcd(c1,m[i]); //如果题目要求要正整数,那么加上一个所有数的最小公倍数
}*/
return c1;
}
int main()
{
int T,n,Case=;
scanf("%d",&n); for(int i=;i<=n;i++) scanf("%lld%lld",&m[i],&r[i]);
// LL c1=1; for(int i=1;i<=n;i++) c1=c1*m[i]/gcd(c1,m[i]);
printf("%lld\n",ex_CRT(n));
return ;
}