孙子定理模板题

如果你不知道什么是中国剩余定理,你可以@

猜数字

现有两组数字,每组\(k\)个,第一组中的数字分别用\(a_1,a_2,\dots,a_k\)表示,第二组中的数字分别用\(b_1,b_2,\dots,b_k\)表示。其中第二组的数字是两两互素的。求最小的\(n\in N\),满足对于\(\forall i\in[i,k]\),有\(b_i|(n-a_i)\)
输入格式
第一行一个整数\(k\)
第二行\(k\)个整数,表示:\(a_1,a_2,\dots,a_k\)
第三行\(k\)个整数,表示:\(b_1,b_2,\dots,b_k\)
输出格式
输出一行一个整数,为所求的答案\(n\)\(1≤k≤10,∣a_i∣≤10^9,1≤b_i≤6×10^3\prod_{i=1}^k b_i\le 10^{18}\)

思路:这是孙子定理的模板题,注意数据范围

#include <bits/stdc++.h>
#include <map>
using namespace std;
typedef long long ll;
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define _for(i, a, b) for (int i=(a); i<=(b); i++)
const int INF = 0x7fffffff;
const int MAXN = 1e5 + 10;

ll m[MAXN], a[MAXN], n;

void exgcd(ll a, ll b, ll &x, ll &y){
	if(b == 0){
		x = 1;
		y = 0;
		return ;
	}
	exgcd(b,a%b,x,y);
	ll temp = x;
	x = y;
	y = temp - a / b * y;
	return ;
}

ll qmul(ll n, ll a, ll mod){
	ll res = 0;
	a = a % mod;
	while(n){
		if(n & 1) res = (res + a) % mod;
		n >>= 1;
		a = (a + a) % mod;
	}
	return res % mod;
}

ll CHT(){
	ll res = 0, M = 1;
	for(int i = 1; i <= n; i++){
		M *= m[i];
	}
	for(int i = 1; i <= n; i++){
		ll x, y;
		ll Mi = M / m[i];
		exgcd(Mi,m[i],x,y);
		x = (x % m[i] + m[i]) % m[i];//防止x为负数
		res = (res + qmul(qmul(Mi,x,M),a[i],M)) % M;//使用快速乘不然有的会爆long long
	}
	if(res < 0) res += M;
	return res;
}

int main(){
	cin >> n;
	for(ll i = 1; i <= n; i++){
		cin >> a[i];
	}
	for(int i= 1; i <= n; i++){
		cin >> m[i];
	}
	for(int i = 1; i <= n; i++){
		a[i] = (a[i] % m[i] + m[i]) % m[i];
	}
	cout << CHT();
	return 0;
}

扩展中国剩余定理

给定\(n\)组非负整数\(a_i,b_i\),求解关于\(x\)的方程组的最小非负整数解。

\[\begin{cases} x\equiv b_1(mod\:a_1)\\ x\equiv b_2(mod\:a_2)\\ \dots\\ x\equiv b_n(mod\:a_n)\\ \end{cases} \]

输入格式
输入一行包含整数\(n\)
接下来\(n\)行,每行两个非负整数\(a_i,b_i\)
输出格式
输出一行,为满足条件的最小非负整数\(x\)
数据范围
\(1\leq n\leq10^5,1\leq b_i,a_i\leq10^{12}\),保证所有\(a_1\)的最小公倍数不超过\(10^{18}\)

思路:同样这也是一道模板题,但是我们要注意数据的范围大小,在编写代码时,使用乘法运算结果会有溢出。

#include <bits/stdc++.h>
#include <map>
using namespace std;
typedef long long ll;
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define _for(i, a, b) for (int i=(a); i<=(b); i++)
const int INF = 0x7fffffff;
const int MAXN = 1e5 + 10;

ll n, m[MAXN], a[MAXN];

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 temp = x;
    x = y;
    y = temp - a / b * y;
    return gcd;
}

ll qmul(ll n, ll b, ll mod){
    ll res = 0;
    while(b > 0){//这里使用b是因为防止n为负数
        if(b & 1) res = (res + n) % mod;
        n = (n + n) % mod;
        b >>= 1;
    }
    return res;
}

int main(){
    IOS
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> m[i] >> a[i];
    }
    ll m1 = m[1], a1 = a[1];    
    ll x, y;
    int ok  = 1;
    for(int i = 2; i <= n; i++){

        ll a2 = a[i], m2 = m[i];
        ll c = ((a2 - a1) % m2 + m2) % m2;//防止c为负数
        ll gcd = exgcd(m1,m2,x,y);
        if(c % gcd){ ok = 0; break;}
        x = qmul(x,c/gcd,m2);//防止爆long long
        a1 = a1 + x * m1;
        m1 = m2 / gcd * m1;//先除后成,防止爆long long
        a1 = (a1 + m1) % m1;
    }
    if(ok == 0) cout << -1;
    else    cout << a1;
    return 0;
}
posted @ 2021-05-29 13:01  h星宇  阅读(129)  评论(0编辑  收藏  举报