关于 GCD

GCD

正常的 gcd

  相信大家都会辗转相除吧 qwq。

更相减损

  首先有一个引理: gcd ⁡ ( a , b ) = gcd ⁡ ( a − b , b ) \gcd(a, b) = \gcd(a - b, b) gcd(a,b)=gcd(ab,b)

  证明: 令 a = k 1 gcd ⁡ ( a , b ) , b = k 2 gcd ⁡ ( a , b ) a = k_1\gcd(a, b), b = k_2\gcd(a, b) a=k1gcd(a,b),b=k2gcd(a,b) 显然有 gcd ⁡ ( k 1 , k 2 ) = 1 \gcd(k_1, k_2) = 1 gcd(k1,k2)=1。然后 gcd ⁡ ( a − b , a ) = gcd ⁡ ( ( k 1 − k 2 ) gcd ⁡ ( a , b ) , k 2 gcd ⁡ ( a , b ) ) = gcd ⁡ ( a , b ) gcd ⁡ ( k 1 − k 2 , k 2 ) \gcd(a - b, a) = \gcd((k_1 - k_2)\gcd(a, b), k_2\gcd(a, b)) = \gcd(a, b) \gcd(k_1 - k_2, k_2) gcd(ab,a)=gcd((k1k2)gcd(a,b),k2gcd(a,b))=gcd(a,b)gcd(k1k2,k2),现在我们就只需要证明 gcd ⁡ ( k 1 − k 2 , k 2 ) = 1 \gcd(k_1 - k_2, k_2) = 1 gcd(k1k2,k2)=1 就行了。

  所以我们考虑反证法,假设 k 1 − k 2 = a x , k 2 = b x k_1- k_2 = ax, k_2 = bx k1k2=ax,k2=bx 其中 x = gcd ⁡ ( k 1 − k 2 , k 2 ) ≠ 1 x = \gcd(k_1 - k_2, k_2) \neq 1 x=gcd(k1k2,k2)=1。那么我们发现 k 1 = k 1 − k 2 + k 2 = ( a + b ) x k_1 = k_1 - k_2 + k_2 = (a + b)x k1=k1k2+k2=(a+b)x,那么 k 1 k_1 k1 k 2 k_2 k2 就有了共同的不等于 1 1 1 的因数,所以 gcd ⁡ ( k 1 , k 2 ) ≠ 1 \gcd(k_1, k_2) \neq 1 gcd(k1,k2)=1 这就和我们的前提矛盾了,所以 gcd ⁡ ( k 1 − k 2 , k 2 ) = 1 \gcd(k_1 - k_2, k_2) = 1 gcd(k1k2,k2)=1

  证毕。

辗转相除

  有了刚才的那个引理,我们就能很自然的推出下面这个式子:

gcd ⁡ ( a , b ) = gcd ⁡ ( b , a m o d    b ) \gcd(a,b) = \gcd(b, a \mod b) gcd(a,b)=gcd(b,amodb)

  这个就是欧几里得求 gcd ⁡ \gcd gcd 的核心式子了(正确性不用解释了吧qwq),于是我们就能写出求 gcd ⁡ \gcd gcd 的非常简便的,只有一行的,正确的,毋庸置疑的,形而上的,代码了:

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

  这个代码的复杂度就显然是 O ( log ⁡ a ) O(\log a) O(loga) 的,因为每次至少除以 2 2 2

更快的 gcd

  这里是 O ( 值域 ) O(值域) O(值域) 预处理 O ( 1 ) O(1) O(1) 查询的 gcd ⁡ \gcd gcd

几个引理

A \Alpha A

  引理一:可以将值域中的每一个数 x x x 分解为 x = a × b × c x = a \times b \times c x=a×b×c,满足要么 a , b , c ≤ x a, b, c \leq \sqrt x a,b,cx ,要么 a , b , c ∈ p r i m e a, b, c \in prime a,b,cprime,其中 p r i m e prime prime 表示质数集。

  证明:我们考虑找到 x x x 的最小值因子 p p p,并找到 x p \frac xp px 的分解,然后把 p p p 乘到 x p \frac xp px 的分解中的最小的那个数上面并成为 x x x 的分解,这样就能满足上述条件(因为 x x x 是质数的情况非常显然,所以只考虑 x x x 不是质数时)。

  考虑归纳法证明上述做法的正确性,假设 x p \frac xp px 的分解是 { a , b , c } \{a, b, c\} {a,b,c} a ≤ b ≤ c a \leq b \leq c abc,那么 a ≤ x p 3 a \leq \sqrt[3]{\frac xp} a3px ,那么(这里只需要证明 a p ≤ x ap \leq \sqrt x apx 就可以了):

a p ≤ p x p 3 ap \leq p\sqrt[3]{\frac xp} app3px

  分两种情况:

  1. p ≤ x 4 p \leq \sqrt[4]{x} p4x 时,有 a p ≤ x 4 x x 4 3 = x ap \leq \sqrt[4]{x} \sqrt[3]{\frac x{\sqrt[4]{x}}} = \sqrt x ap4x 34x x =x 这时显然成立。
  2. p > x 4 p > \sqrt[4]{x} p>4x 时,还要分两种情况考虑:
    1) a = 1 a = 1 a=1,那么显然就是 a p = p ≤ x ap = p \leq \sqrt x ap=px
    2) a ≠ 1 a \neq 1 a=1,我们令 n p \frac np pn 的最小质因子为 q q q,那么显然有 p ≤ q ≤ a ≤ b ≤ c p \leq q \leq a \leq b \leq c pqabc,于是 p a b c ≥ p 4 > x 4 = x pabc \geq p^4 > \sqrt[4]x = x pabcp4>4x =x,这就和前面 q a b c = x qabc = x qabc=x 的前提矛盾了,所以只能有 p ≤ x 4 p \leq \sqrt[4] x p4x
B \Beta B

  第二个引理:对于一个询问 gcd ⁡ ( x , y ) \gcd(x, y) gcd(x,y),我们已经把 x x x 拆分成了 a , b , c a, b, c a,b,c,那么进行以下操作得到的答案与 gcd ⁡ ( x , y ) \gcd(x, y) gcd(x,y) 相同:

i = gcd(a, y), y /= i;
j = gcd(b, y), y /= j;
k = gcd(c, y), y /= k;
ans = i * j * k;

  这个玩意儿的正确性就非常显然了,所以就不放证明了qwq。

算法的基本流程

  对于一个询问 gcd ⁡ ( a , b ) \gcd(a, b) gcd(a,b),我们就考虑用引理 B \Beta B 来进行计算,那么我们就可以先考虑 gcd ⁡ ( x , a ) \gcd(x, a) gcd(x,a),答案分三种情况讨论:

  1. x ≤ n x \leq \sqrt n xn ,考虑手动递归一层(辗转相除): gcd ⁡ ( x , a ) = gcd ⁡ ( x , a m o d    x ) \gcd(x, a) = \gcd(x, a \mod x) gcd(x,a)=gcd(x,amodx),这里就有 x ≤ n x \leq \sqrt n xn a m o d    x ≤ n a \mod x \leq \sqrt n amodxn ,所以我们可以考虑打一个 n × n \sqrt n \times \sqrt n n ×n 的表,记录 gcd ⁡ ( i , j ) \gcd(i, j) gcd(i,j) 的值,然后就可以 O ( 1 ) O(1) O(1) 查询了。
  2. x > n ∧ x ∣ a x > \sqrt n \land x \mid a x>n xa,这个时候答案显然为 x x x
  3. x > n ∧ x ∤ a x > \sqrt n \land x \nmid a x>n xa,这个时候,根据引理 A \Alpha A,我们知道 x x x 此时必定是一个质数,所以必定有 gcd ⁡ ( x , a ) = 1 \gcd(x, a) = 1 gcd(x,a)=1

  现在我们发现,如果把 n × n \sqrt n \times \sqrt n n ×n gcd ⁡ \gcd gcd 表打出来并且解决数的拆分,然后就可以 O ( 1 ) O(1) O(1) 计算 gcd ⁡ \gcd gcd 了。

  拆分的话就按照我们的 A \Alpha A 引理的证明中提到的方案直接构造就好了,时间复杂度时 O ( n ) O(n) O(n) 的(线性筛)。

代码

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define MAXN 5050
#define MAXM 1001000
#define MAXK 1010
#define mod 998244353

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' or c > '9') c = getchar();
	while('0' <= c and c <= '9'){
		x = x * 10 + c - '0'; c = getchar();
	}
	return x;
}

int n = 0; int tot = 0;
int a[MAXN] = { 0 };
int b[MAXN] = { 0 };
int fac[MAXM][3] = { 0 };                          // 分解 
int pre[MAXK][MAXK] = { 0 };                       // 预处理的 gcd 数组 
int vis[MAXM] = { 0 };
int pri[MAXM] = { 0 };

void prework(int N, int K){
	fac[1][0] = fac[1][1] = fac[1][2] = 1;
	for(int i = 2; i <= N; i++){
		if(!vis[i]) pri[++tot] = i, fac[i][0] = fac[i][1] = 1, fac[i][2] = i;
		for(int j = 1; j <= tot and i * pri[j] <= N; j++){
			int temp = i * pri[j];
			vis[temp] = 1;
			fac[temp][0] = fac[i][0] * pri[j];
			fac[temp][1] = fac[i][1];
			fac[temp][2] = fac[i][2];
			if (fac[temp][0] > fac[temp][1]) swap(fac[temp][0], fac[temp][1]);
	      	if (fac[temp][1] > fac[temp][2]) swap(fac[temp][1], fac[temp][2]);
			if(i % pri[j] == 0) break;
		}
	}
	for(int i = 0; i <= K; i++) pre[i][0] = pre[0][i] = i;
	for(int i = 1; i <= K; i++)
		for(int j = 1; j <= i; j++)
			pre[i][j] = pre[j][i] = pre[j][i % j];
}

int gcd(int x, int y){
	int ans = 1;
	for(int i = 0; i < 3; i++){
		int d = 0;
		if(fac[x][i] <= 1e3) d = pre[fac[x][i]][y % fac[x][i]];
		else if(y % fac[x][i] != 0) d = 1;
		else d = fac[x][i];
		y /= d, ans *= d;
	}
	return ans;
}

int main(){
	prework(1e6, 1e3);
	n = in;
	for(int i = 1; i <= n; i++) a[i] = in;
	for(int i = 1; i <= n; i++) b[i] = in;
	for(int i = 1; i <= n; i++){
		int ans = 0;
		for(int j = 1, now = i; j <= n; j++, now = 1ll * now * i % mod)
			ans = (ans + (1ll * now * gcd(a[i], b[j]) % mod) % mod) %mod;
		cout << ans << '\n';
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值