[学习笔记]基于值域预处理的快速 GCD

江湖人称 \(O(1)gcd\) 的操作。

我们用线性筛处理,那么我们可以把大多数的分解成不大于\(O(\sqrt n)\)的数的乘积
如果分解出来大于\(O(\sqrt n)\),其一定是一个质数。
我们具体方法:
我们先找到\(x\)的最小质因子 \(p\) ,并得到 \(\frac{x}{p}\) 的分解,然后把 \(p\) 乘到最小的数上。

证明为什么是对的。

考虑归纳法。

我们证明把 \(p\) 乘上最小的数其结果小于 \(\sqrt n\)

则有\(\frac{n}{p}\) 的分解最小的数一定小于\(a \leq \sqrt[3]{\frac{n}{p}}\)

所以有 \(a \times p \leq \sqrt[3]{\frac{n}{p}} \times p\)

\(p > \sqrt[4]{n}\),则分解出的数为其三个质因子。

否则有 \(a \times p \leq \sqrt[3]{\frac{n}{\sqrt[4]{n}}} \times \sqrt[4]{n} = \sqrt n\)

然后打一个\(O(\sqrt v)\)
然后打一个\(O(\sqrt v) \times O(\sqrt v)\)的表。

具体看代码。

#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
#define N 5010
#define V 1000010
#define radio 1010

int a[N],b[N],n,ans;

int np[V],pri[V],cnt;

int k[V][3];

int _gcd[radio][radio];

inline int gcd(int a,int b){
	int g = 1;
	for(int tmp,i = 0;i < 3;++i){
		if(k[a][i] > radio){
			if(b % k[a][i] == 0)
			tmp = k[a][i];
			else
			tmp = 1;
		}else{
			tmp = _gcd[k[a][i]][b % k[a][i]];
		}
		b /= tmp;
		g *= tmp;
	}
	return g;
}

int main(){
	k[1][0] = k[1][1] = k[1][2] = 1;
	np[1] = 1;
//    for(int i = 2; i < V; i++) {
//        if(!np[i]) pri[++cnt] = i, k[i][2] = i, k[i][1] = k[i][0] = 1;
//        for(int j = 1; pri[j] * i < V; j++) {
//            np[i * pri[j]] = 1;
//            int *tmp = k[i * pri[j]];
//            tmp[0] = k[i][0] * pri[j];
//            tmp[1] = k[i][1];
//            tmp[2] = k[i][2];
//            if(tmp[1] < tmp[0]) std::swap(tmp[1], tmp[0]);
//            if(tmp[2] < tmp[1]) std::swap(tmp[2], tmp[1]);
//            if(i % pri[j] == 0) break;
//        }
//    }
	for(int i = 2;i < V;++i){
		if(!np[i])pri[++cnt] = i,k[i][2] = i,k[i][1] = k[i][0] = 1;
		for(int j = 1;pri[j] * i < V && j <= cnt;++j){
			np[i * pri[j]] = 1;
			int *tmp = k[i * pri[j]];
			tmp[0] = k[i][0] * pri[j];
			tmp[1] = k[i][1];
			tmp[2] = k[i][2];
			if(tmp[1] < tmp[0])std::swap(tmp[1],tmp[0]);
			if(tmp[2] < tmp[1])std::swap(tmp[2],tmp[1]);
			if(i % pri[j] == 0)break;
		}
	}
	for(int i = 1;i < radio;++i)_gcd[i][0] = _gcd[0][i] = i;
	for(int _max = 1;_max < radio;_max ++ ){
		for(int i = 1;i <= _max;++i)
		_gcd[i][_max] = _gcd[_max][i] = _gcd[_max % i][i];
	}
// for(int i = 1; i <= 10; i++)
//         for(int j = 1; j <= 10; j++) printf("gcd(%d, %d) = %d\n", i, j, _gcd[i][j]);
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", a + i);
    for(int i = 1; i <= n; i++) scanf("%d", b + i);
    for(int i = 1; i <= n; i++) {
        int now = 1, ans = 0;
        for(int j = 1; j <= n; j++) {
            now = 1ll * now * i % mod;
            ans = (ans + 1ll * gcd(a[i], b[j]) * now) % mod;
        }
        printf("%d\n", ans);
    }
    return 0;

}
posted @ 2021-12-21 22:33  fhq_treap  阅读(80)  评论(0编辑  收藏  举报