数学知识学习(持续更新)(提高组)

数学知识学习(持续更新)

Ⅰ 质数

⒈ 试除法

⑴ 判定素数
时间复杂度: Θ ( n ) \Theta(\sqrt{n}) Θ(n )
#include <bits/stdc++.h>
using namespace std;

bool isPrime(int x)
{
	if (x < 2)
		return false;
	for (int i = 2; i <= x / i; i ++ )
	{
		if (x % i == 0)
			return false;
	}
	return true;
}

int main()
{
	int n;
	cin >> n;
	while (n -- )
	{
		int x;
		cin >> x;
		if (isPrime(x))
			cout << "Yes" << endl;
		else
			cout << "No" << endl;
	}
	return 0;
}
⑵ 分解质因数
时间复杂度: Θ ( log ⁡ n ) \Theta(\log n) Θ(logn) Θ ( n ) \Theta(\sqrt{n}) Θ(n )之间
#include <bits/stdc++.h>
using namespace std;

void isPrime(int x)
{
	for (int i = 2; i <= x / i; i ++ )
	{
		if (x % i == 0)
		{
			int s = 0;
			while (x % i == 0)
				x /= i, ++s;
			cout << i << " " << s << "\n";
		}
	}
	if (x > 1)
		cout << x << " " << 1 << "\n";
	cout << "\n";
}

int main()
{
	int n;
	cin >> n;
	while (n -- )
	{
		int x;
		cin >> x;
		isPrime(x);
	}
	return 0;
}

⒉ 埃氏筛

原理:

1 1 1 ~ n n n 排成一行,从第1个开始,在每个数上向后把这个数的倍数全部筛掉,这样就可以只剩下质数了。

时间复杂度: Θ ( n log ⁡ log ⁡ n ) \Theta(n\log \log n) Θ(nloglogn)

附:一般, log ⁡ log ⁡ n \log \log n loglogn 会忽略不计,也就是说,时间复杂度近似 Θ ( n ) \Theta(n) Θ(n)。但是,真正能做到 Θ ( n ) \Theta(n) Θ(n) 的算法是下一个算法——线性筛。

Code - 模板
int primes[N], cnt;
bool st[N];

void ass(int n)//埃氏筛模板
{
	for (int i = 2; i <= n; ++i)
	{
		if (!st[i])
		{
			primes[cnt++] = n;
			for (int j = i + i; j <= n; j += i)
				st[j] = true;
		}
	}
}
Code - 用法
int main()
{
	int n;
	cin >> n;
	ass(n);
	cout << cnt;
	return 0;
}

我们发现这里面似乎会对某些数标记了很多次其为合数。有没有什么办法省掉无意义的步骤呢?请看下一个算法!

⒊ 线性筛法(埃氏筛优化)

优化方式:

我们在上一个算法中提到埃氏筛会对某些数标记了很多次其为合数,如果能让每个合数都只被标记一次,那么时间复杂度就可以降到 Θ ( n ) \Theta(n) Θ(n)了。

时间复杂度: Θ ( n ) \Theta(n) Θ(n)
Code - 模板
const int N = 1000010;
int primes[N], cnt;
bool st[N];

void xxs(int n)//线性筛模板
{
	for (int i = 2; i <= n; ++i)
	{
		if (!st[i])
			primes[cnt++] = i;
		for (int j = 0; primes[j] <= n / i; ++j)
		{
			st[primes[j]*i] = true;
			if (i % primes[j] == 0)
				break;
		}
	}
}
Code - 用法
int main()
{
	int n;
	cin >> n;
	xxs(n);
	cout << cnt;
	return 0;
}
分析

对于代码 if(i%primes[j]==0)break; n n n 只会被最小的质因子筛掉。
证明如下:

这里,我们分两种情况来讨论。

  1. i%primes[j]==0,则 primes[j] 一定是 i 的最小质因子,primes[j] 也一定是 primes[j]*i 的最小质因子。
  2. i%primes[j]!=0,则 primes[j] 一定小于 i 的所有质因子,primes[j] 也一定是 primes[j]*i 的最小质因子。

证毕!

Ⅱ 约数

⒈ 试除法求约数

时间复杂度: Θ ( n ) \Theta(\sqrt{n}) Θ(n )
Code - 模板
vector<int> get_divisors(int n)\\试除法求约数模板
{
	vector<int> res;
	for (int i = 1; i <= n / i; ++i)
	{
		if (n % i == 0)
		{
			res.push_back(i);
			if (i != n / i)
				res.push_back(n / i);
		}
	}
	sort(res.begin(), res.end());\\不必要时可以不加
	return res;
}

注意:这个模板求的约数是以 vector 的类型返回的,所以,在使用时,要小心。具体使用方法看下面的用法代码。

Code - 用法
int x;
cin >> x;
auto res = get_divisors(x);
for (auto t : res)
	cout << t << " ";

⒉ 约数个数与约数和专题

0 注

本小节的内容部分摘自这里

⑴ 基本定理

算术基本定理:设 n = p 1 r 1 p 2 r 2 p 3 r 3 ⋯ p k r k n={p_1}^{r_1}{p_2}^{r_2}{p_3}^{r_3}\cdots{p_k}^{r_k} n=p1r1p2r2p3r3pkrk

约数个数公式: d ( n ) = ( r 1 + 1 ) × ( r 2 + 1 ) × ( r 3 + 1 ) × ⋯ × ( r k + 1 ) d(n)=(r_1+1)\times(r_2+1)\times(r_3+1)\times\cdots\times(r_k+1) d(n)=(r1+1)×(r2+1)×(r3+1)××(rk+1)

约数和公式: σ ( n ) = ∏ i = 1 k ( ∑ j = 0 r i p i j ) \sigma(n)=\prod\limits_{i=1}^k\left(\sum\limits_{j=0}^{r_i}{p_i}^{j}\right) σ(n)=i=1k(j=0ripij)

⑵ 求单个数的约数个数
#include <bits/stdc++.h>

using namespace std;
typedef long long LL;

/**
 * 功能:计算约数个数
 * @param n
 * @return
 */
LL getDivisorCount(LL x)
{
    unordered_map<int, int> primes; //key:质数 value:个数
    //求质数因子
    for (int i = 2; i <= x / i; i++)
        while (x % i == 0) x /= i, primes[i]++; //primes[i]表示质数i因子的个数+1
    //如果还有质数,那就加上
    if (x > 1) primes[x]++;
    //公式大法
    LL res = 1;
    for (auto p: primes) res = res * (p.second + 1);
    return res;
}

LL res;

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cout << i << " " << getDivisorCount(i) << endl;
    return 0;
}
⑶ 求数字连乘积的约数个数
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;

int main()
{
	int n;
	cin >> n;
	unordered_map<int, int> primes;
	while (n--)
	{
		int x;
		cin >> x;
		for (int i = 2; i <= x / i; ++i)
		{
			while (x % i == 0)
			{
				x /= i;
				++primes[i];
			}
		}
		if (x > 1)
			++primes[x];
	}
	long long res = 1;
	for (auto prime : primes)
		res = res * (prime.second + 1) % mod;
	cout << res;
}
⑷ 求单个数字的约数和
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/**
 * 功能:计算约数之和
 * @param n
 * @return
 */
LL getSumOfDivisors(LL x)
{
    //拆出所有质数因子及质数因子个数
    unordered_map<int, int> primes;
    for (int i = 2; i <= x / i; i++)
        while (x % i == 0)
        {
            x /= i;
            primes[i]++;
        }
    if (x > 1) primes[x]++;
    //计算约数个数
    LL res = 1;
    for (auto p : primes)
    {
        LL a = p.first, b = p.second;
        LL t = 1;
        while (b--) t = (t * a + 1);
        res = res * t;
    }
    return res;
}
LL res;
int main()
{
    int n;
    cin >> n;
    cout<<getSumOfDivisors(n) << endl;
    return 0;
}
⑸ 求数字连乘积的约数和
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
unordered_map<int, int> primes;

int main()
{
	int n, x;
	cin >> n;
	while (n--)
	{
		cin >> x;
		for (int i = 2; i <= x / i; i++)
		{
			while (x % i == 0)
			{
				primes[i]++;
				x /= i;
			}
		}
		if (x > 1)
			primes[x]++;
	}
	ll res = 1;
	for (auto p : primes)
	{
		ll a = p.first, b = p.second;
		ll t = 1;
		while (b--)
			t = (t * a + 1) % mod;
		res = res * t % mod;
	}
	cout << res << endl;
	return 0;
}
⑹ 筛法求区间内的约数个数与约数和
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/**
 * 功能:线性筛出约数个数与约数和
 * Tag:模板,约数个数,约数和
 */
const int N = 1e6 + 10;
int n;
int primes[N];      //质数数组
int idx;            //质数数组下标游标
bool st[N];         //是否已被筛出
int d[N];           //约数个数数组
int sigma[N];       //约数和数组
void get_divisor(int n)
{
    //积性函数的出发值
    d[1] = sigma[1] = 1;
    for (int i = 2; i <= n; i++)  //倍数
    {
        if (!st[i])primes[++idx] = i, d[i] = 2, sigma[i] = i + 1;
        for (int j = 1; i * primes[j] <= n & j <= idx; j++)
        {
            st[i * primes[j]] = true;
            d[i * primes[j]] = d[i] << 1;
            sigma[i * primes[j]] = sigma[i] * (primes[j] + 1);
            if (i % primes[j] == 0)
            {
                d[i * primes[j]] -= d[i / primes[j]];
                sigma[i * primes[j]] -= primes[j] * sigma[i / primes[j]];
                break;
            }
        }
    }
}
LL res;
int main()
{
    cin >> n;
    //开始筛约数个数,约数和
    get_divisor(n);
    //输出约数个数和
    for (int i = 1; i <= n; i++) res += d[i];
    cout << res << endl;
    return 0;
}
⑺辗转相除法求最大公约数
#include <bits/stdc++.h>
using namespace std;

int gcd(int a, int b)
{
	return b ? gcd(b, a % b) : a;
}

int main()
{
	int n;
	cin >> n;
	while (n -- )
	{
		int a, b;
		scanf("%d%d", &a, &b);
		printf("%d\n", gcd(a, b));
	}
	return 0;
}

Ⅲ 欧拉函数

定义

在数论,对正整数 n n n,欧拉函数是小于等于 n n n 的正整数中与 n n n 互质的数的数目。

定理

n = p 1 r 1 p 2 r 2 p 3 r 3 ⋯ p k r k n={p_1}^{r_1}{p_2}^{r_2}{p_3}^{r_3}\cdots{p_k}^{r_k} n=p1r1p2r2p3r3pkrk

那么 φ ( n ) = n × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) × ⋯ × ( 1 − 1 p k ) \varphi(n) = n\times\left(1 - \dfrac{1}{p_1}\right) \times \left(1 - \dfrac{1}{p_2}\right) \times\cdots\times\left(1 - \dfrac{1}{p_k}\right) φ(n)=n×(1p11)×(1p21)××(1pk1)

Code - 求单个数的欧拉函数

#include <bits/stdc++.h>
using namespace std;

int main()
{
	int n;
	cin >> n;
	while (n--)
	{
		int a;
		cin >> a;
		int res = a;
		for (int i = 2; i <= a / i; ++i)
		{
			if (a % i == 0)
			{
				res = res / i * (i - 1);
				while (a % i == 0)
					a /= i;
			}
		}
		if (a > 1)
			res = res / a * (a - 1);
		cout << res << endl;
	}
	return 0;
}

1 1 1 n n n 的欧拉函数之和(欧拉筛)

分析

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n, phi[N], prime[N], cnt;
bool st[N];

long long get_euler(int x)
{
	phi[1] = 1;
	for (int i = 2; i <= x; i++)
	{
		if (!st[i])
		{
			prime[cnt++] = i;
			phi[i] = i - 1;
		}
		for (int j = 0; prime[j] <= x / i; j++)
		{
			st[i * prime[j]] = true;
			if (i % prime[j] == 0)
			{
				phi[i * prime[j]] = prime[j] * phi[i];
				break;
			}
			else
				phi[i * prime[j]] = (prime[j] - 1) * phi[i];
		}
	}
	long long res = 0;
	for (int i = 1; i <= x; i++)
		res += phi[i];
	return res;
}

int main()
{
	cin >> n;
	cout << get_euler(n) << endl;
	return 0;
}

Ⅳ 快速幂

⒈快速幂

让我们先来思考一个问题: 7 10 7^{10} 710 怎样算比较快?

方法1:最朴素的想法是 7 × 7 = 49 , 49 × 7 = 343 , ⋯ 7\times7=49, 49\times7=343, \cdots 7×7=49,49×7=343,,一步一步算,共进行了 9 9 9 次乘法。这样算无疑太慢了,尤其对计算机而言,每次运算只乘上一个个位数,无疑太屈才了。这时我们想到,也许可以拆分问题。

方法2:先算 7 5 7^5 75,再算它的平方,共进行了 5 5 5 次乘法。但这并不是最优解,因为对于 7 5 7^5 75,我们仍然可以拆分问题。

方法3:先算 7 × 7 = 49 7\times7=49 7×7=49,则 7 5 = 49 × 49 × 7 7^5=49\times49\times7 75=49×49×7,再算它的平方,共进行了 4 4 4 次乘法。

模仿这样的过程,我们得到一个在 Θ ( log ⁡ n ) \Theta(\log n) Θ(logn) 时间内计算出幂的算法,也就是快速幂

递归快速幂

递归方程

a n = { a n − 1 ⋅ a , if  n  is odd a n 2 ⋅ a n 2 , if  n  is even but not 0 1 , if  n = 0 a^n=\begin{cases}a^{n-1}\cdot a,&\text{if }n\text{ is odd}\\a^{\frac{n}{2}}\cdot a^{\frac{n}{2}},&\text{if }n\text{ is even but not 0}\\1,&\text{if }n=0\end{cases} an=an1a,a2na2n,1,if n is oddif n is even but not 0if n=0

Code

//递归快速幂
int qpow(int a, int n)
{
    if (n == 0)
        return 1;
    else if (n % 2 == 1)
        return qpow(a, n - 1) * a;
    else
    {
        int temp = qpow(a, n / 2);
        return temp * temp;
    }
}
//递归快速幂(对大素数取模)
#define MOD 1000000007
typedef long long ll;
ll qpow(ll a, ll n)
{
    if (n == 0)
        return 1;
    else if (n % 2 == 1)
        return qpow(a, n - 1) * a % MOD;
    else
    {
        ll temp = qpow(a, n / 2) % MOD;
        return temp * temp % MOD;
    }
}

大家知道,递归虽然简洁,但会产生额外的空间开销。我们可以把递归改写为循环,来避免对栈空间的大量占用,也就是非递归快速幂

非递归快速幂

Code

//非递归快速幂
int qpow(int a, int n){
    int ans = 1;
    while(n){
        if(n&1)        //如果n的当前末位为1
            ans *= a;  //ans乘上当前的a
        a *= a;        //a自乘
        n >>= 1;       //n往右移一位
    }
    return ans;
}
快速幂模板题

[acwing] 875 - 快速幂

#include <bits/stdc++.h>
using namespace std;

inline int read()
{
	register int x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9')
	{
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9')
	{
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	return x * f;
}

inline long long qmi(int a, int k, int p)
{
	long long res = 1;
	while (k)
	{
		if (k & 1)
			res = (long long) res * a % p;
		k >>= 1;
		a = (long long) a * a % p;
	}
	return res;
}

int main()
{
	register int n = read();
	while (n--)
	{
		register int a, k, p;
		a = read(), k = read(), p = read();
		printf("%d\n", qmi(a, k, p));
	}
	return 0;
}

⒉ 快速幂求乘法逆元

定义

若整数 b , m b,m b,m 互质,并且 b ∣ a b\mid a ba,则存在一个整数 x x x,使得 a b ≡ a ⋅ x   ( m o d m ) \frac{a}{b}\equiv a\cdot x\ \pmod{m} baax (modm),则称 x x x b b b 的模 m m m 乘法逆元,记为 b − 1   ( m o d m ) b^{-1}\ \pmod{m} b1 (modm)

b b b 存在乘法逆元的充要条件是 b b b 与模数 m m m 互质。当模数 m m m 为质数时, b m − 2 b^{m-2} bm2 即为 b b b 的乘法逆元。

模板

[acwing] 876 - 快速幂求逆元

#include <bits/stdc++.h>
using namespace std;

int qmi(int a, int k, int p)
{
	int res = 1 % p;
	while (k)
	{
		if (k & 1)
			res = (long long)res * a % p;
		a = (long long)a * a % p;
		k >>= 1;
	}
	return res;

}

int main()
{
	int n;
	scanf("%d", &n);
	for (register int i(1); i <= n; ++i)
	{
		int a, b;
		scanf("%d%d", &a, &b);
		if (a % b == 0)
			printf("impossible\n");
		else
			printf("%d\n", qmi(a, b - 2, b));
	}
	return 0;
}

⒊ 扩展欧几里得算法(exgcd)

⑴ 裴蜀定理

裴蜀定理,又称贝祖定理(Bézout’s lemma)。是一个关于最大公约数的定理。

其内容是:设 a , b a,b a,b 是不全为零的整数,则存在整数 x , y x,y x,y, 使得 a x + b y = gcd ( a , b ) ax+by=\text{gcd}\left(a,b\right) ax+by=gcd(a,b).

证明

若任何一个等于 0 0 0, 则 gcd ( a , b ) = a \text{gcd}\left(a,b\right)=a gcd(a,b)=a. 这时定理显然成立。

a , b a,b a,b 不等于 0 0 0.

由于 gcd ( a , b ) = gcd ( a , − b ) \text{gcd}\left(a,b\right)=\text{gcd}\left(a,-b\right) gcd(a,b)=gcd(a,b),

不妨设 a , b a,b a,b 都大于 0 0 0 a ≥ b , gcd ( a , b ) = d a\geq b,\text{gcd}\left(a,b\right)=d ab,gcd(a,b)=d.

a x + b y = d ax+by=d ax+by=d, 两边同时除以 d d d, 可得 a 1 x + b 1 y = 1 a_1x+b_1y=1 a1x+b1y=1, 其中 ( a 1 , b 1 ) = 1 \left(a_1,b_1\right)=1 (a1,b1)=1.

转证 a 1 x + b 1 y = 1 a_1x+b_1y=1 a1x+b1y=1.

我们先回顾一下辗转相除法是怎么做的,由 gcd ( a , b ) → gcd ( b , a  mod  b ) → ⋯ \text{gcd}\left(a,b\right)\to\text{gcd}\left(b,a\text{ mod }b\right)\to\cdots gcd(a,b)gcd(b,a mod b) 我们把模出来的数据叫做 r r r 于是,有

gcd ( a 1 , b 1 ) = gcd ( b 1 , r 1 ) = gcd ( r 1 , r 2 ) = ⋯ = ( r n − 1 , r n ) = 1 \text{gcd}\left(a_1,b_1\right)=\text{gcd}\left(b_1,r_1\right)=\text{gcd}\left(r_1,r_2\right)=\cdots=\left(r_{n-1},r_n\right)=1 gcd(a1,b1)=gcd(b1,r1)=gcd(r1,r2)==(rn1,rn)=1

把辗转相除法中的运算展开,做成带余数的除法,得

不妨令辗转相除法在除到互质的时候退出则 r n = 1 r_n=1 rn=1 所以有( q q q 被换成了 x x x,为了符合最终形式)

r n − 2 = x n r n − 1 + 1 r_{n-2}=x_nr_{n-1}+1 rn2=xnrn1+1

1 = r n − 2 − x n r n − 1 1=r_{n-2}-x_nr_{n-1} 1=rn2xnrn1

由倒数第三个式子 r n − 1 = r n − 3 − x n − 1 r n − 2 r_{n-1}=r_{n-3}-x_{n-1}r_{n-2} rn1=rn3xn1rn2 代入上式,得

1 = r n − 2 ( 1 + x n x n − 1 ) − x n r n − 3 1=r_{n-2}\left(1+x_nx_{n-1}\right)-x_nr_{n-3} 1=rn2(1+xnxn1)xnrn3

然后用同样的办法用它上面的等式逐个地消去 r n − 2 , ⋯   , r 1 r_{n-2},\cdots,r_1 rn2,,r1,

可证得 a 1 x + b 1 y = 1 a_1x+b_1y=1 a1x+b1y=1. 这样等于是一般式中 d = 1 d=1 d=1 的情况。

⑵ 扩展欧几里得算法

扩展欧几里得算法是欧几里得算法(又叫辗转相除法)的扩展。已知整数 a , b a,b a,b,扩展欧几里得算法可以在求得 a , b a,b a,b 的最大公约数的同时,能找到整数 x , y x,y x,y(其中一个很可能是负数),使它们满足裴蜀定理。

模板

int exgcd(int a, int b, int &x, int &y)
{
	if (!b)
	{
		x = 1, y = 0;
		return a;
	}
	int d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}

模板题

[acwing] 876 - 扩展欧几里得算法

Code

#include <bits/stdc++.h>
using namespace std;

int exgcd(int a, int b, int &x, int &y)
{
	if (!b)
	{
		x = 1, y = 0;
		return a;
	}
	int d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}

int main()
{
	int n;
	scanf("%d", &n);
	while (n--)
	{
		int a, b, x, y;
		scanf("%d%d", &a, &b);
		exgcd(a, b, x, y);
		printf("%d %d\n", x, y);
	}
}
⑶ 线性同余方程

解形如 a x ≡ b ( mod  m ) ax≡b(\text{mod }m) axb(mod m) 的方程。

根据上面同余的理论可以得到 a x − b ≡ 0 ( mod  m ) ax-b≡0(\text{mod }m) axb0(mod m)

所以 m ∣ a x − b m|ax-b maxb,设 a x − b = − m k ax-b=-mk axb=mk,可以得到一个不定方程 a x + m k = b ax+mk=b ax+mk=b

解这个不定方程即可。

我们从这可以得出如果 gcd ⁡ ( a , m ) ∤ b \gcd(a,m)∤b gcd(a,m)b 方程无解

Code - 模板

#include <bits/stdc++.h>
using namespace std;

inline int exgcd(int a, int b, int &x, int &y)
{
	if (!b)
	{
		x = 1, y = 0;
		return a;
	}
	int d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}

int main()
{
	int n;
	cin >> n;
	while (n--)
	{
		int a, b, m;
		cin >> a >> b >> m;
		int x, y;
		int d = exgcd(a, m, x, y);
		if (b % d)
			puts("impossible");
		else
			printf("%d\n", (long long) b / d * x % m);
	}
	return 0;
}

⒋ 中国剩余定理

⑴ 算法简介及过程(来自OI Wiki)

中国剩余定理 (Chinese Remainder Theorem, CRT) 可求解如下形式的一元线性同余方程组(其中 n 1 , n 2 , ⋯   , n k n_1,n_2,\cdots,n_k n1,n2,,nk 两两互质):

{ x ≡ a 1 ( m o d n 1 ) x ≡ a 2 ( m o d n 2 ) ⋮ x ≡ a k ( m o d n k ) \begin{cases}x&\equiv a_1\pmod{n_1}\\x&\equiv a_2\pmod{n_2}\\ &\vdots\\x&\equiv a_k\pmod{n_k}\\ \end{cases} xxxa1(modn1)a2(modn2)ak(modnk)

⑵ C 语言代码(来自OI Wiki)
// C++ Version
LL CRT(int k, LL* a, LL* r) {
  LL n = 1, ans = 0;
  for (int i = 1; i <= k; i++) n = n * r[i];
  for (int i = 1; i <= k; i++) {
    LL m = n / r[i], b, y;
    exgcd(m, r[i], b, y);  // b * m mod r[i] = 1
    ans = (ans + a[i] * m * b % mod) % mod;
  }
  return (ans % mod + mod) % mod;
}
⑶ 典型习题

表达整数的奇怪方式

思路

讲解图

代码(最近不知跟哪位大佬学的,离奇的码风↓)

#include<bits/stdc++.h>
#define _abs(x) (x^(x>>31))-(x >> 31)
#define qwq long long exgcd(long long a, long long b, long long &x, long long &y){if (!b){x = 1, y = 0;return a;}long long d = exgcd(b, a % b, y, x);y -= a / b * x;return d;}
using namespace std;
qwq
int main()
{
	int n;
	cin >> n;
	long long x = 0, a1, m1;
	cin >> a1 >> m1;
	for (register int i(0); i < n - 1; ++i)
	{
		long long a2, m2;
		cin >> a2 >> m2;
		long long k1, k2, d = exgcd(a1, -a2, k1, k2);
		if ((m2 - m1) % d)
		{
			x = -1;
			break;
		}
		k1 *= (m2 - m1) / d;
		k1 = (k1 % (a2 / d) + a2 / d) % (a2 / d);
		x = k1 * a1 + m1;
		long long a = _abs(a1 / d * a2);
		m1 = k1 * a1 + m1;
		a1 = a;
	}
	if (x != -1) x = (x % a1 + a1) % a1;
	cout << x << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lvshu · 绿树

非常感谢您的搭讪

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值