孙子定理模板题

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

猜数字

现有两组数字,每组 k k k个,第一组中的数字分别用 a 1 , a 2 , … , a k a_1,a_2,\dots,a_k a1,a2,,ak表示,第二组中的数字分别用 b 1 , b 2 , … , b k b_1,b_2,\dots,b_k b1,b2,,bk表示。其中第二组的数字是两两互素的。求最小的 n ∈ N n\in N nN,满足对于 ∀ i ∈ [ i , k ] \forall i\in[i,k] i[i,k],有 b i ∣ ( n − a i ) b_i|(n-a_i) bi(nai)
输入格式
第一行一个整数 k k k
第二行 k k k个整数,表示: a 1 , a 2 , … , a k a_1,a_2,\dots,a_k a1,a2,,ak
第三行 k k k个整数,表示: b 1 , b 2 , … , b k b_1,b_2,\dots,b_k b1,b2,,bk
输出格式
输出一行一个整数,为所求的答案 n n n 1 ≤ k ≤ 10 , ∣ a i ∣ ≤ 1 0 9 , 1 ≤ b i ≤ 6 × 1 0 3 ∏ i = 1 k b i ≤ 1 0 18 1≤k≤10,∣a_i∣≤10^9,1≤b_i≤6×10^3\prod_{i=1}^k b_i\le 10^{18} 1k10ai∣≤1091bi6×103i=1kbi1018

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

#include 
#include 
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 n n组非负整数 a i , b i a_i,b_i ai,bi,求解关于 x x x的方程组的最小非负整数解。
{ x ≡ b 1 ( m o d   a 1 ) x ≡ b 2 ( m o d   a 2 ) … x ≡ b n ( m o d   a n ) \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} xb1(moda1)xb2(moda2)xbn(modan)
输入格式
输入一行包含整数 n n n
接下来 n n n行,每行两个非负整数 a i , b i a_i,b_i ai,bi
输出格式
输出一行,为满足条件的最小非负整数 x x x
数据范围
1 ≤ n ≤ 1 0 5 , 1 ≤ b i , a i ≤ 1 0 12 1\leq n\leq10^5,1\leq b_i,a_i\leq10^{12} 1n105,1bi,ai1012,保证所有 a 1 a_1 a1的最小公倍数不超过 1 0 18 10^{18} 1018

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

#include 
#include 
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;
}

你可能感兴趣的:(例题,算法,c++,开发语言)