[学习笔记]基于值域预处理的快速 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;
}