1 条题解
-
0
自动搬运
来自洛谷,原作者为

moongazer
We do not know and will not know搬运于
2025-08-24 22:10:43,当前版本为作者最后更新于2020-01-24 00:35:24,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
一种值域时间预处理时间求最大公约数()的算法
$\mathfrak{View\space it\space on\space my\space Blog}$
Update in 2020.08.07:修正了一处错误,感谢@Kinesis的指正
一些约定
- 为询问的值域
- 为全体素数集合
- 集合是的分解,当且仅当
原理
定理一
内容
可以将值域中的每个分解成,满足或(定义这种分解为合法分解)
证明
不妨设若且,则可分解为且有,而则有的分解,若则可按该规律一直分解直到或
定理二
内容
对于询问,分别考虑对答案的贡献,对答案的贡献为,再将除以(这个因子已经被算过,不能重复计算),再对干同样的事,三个贡献相乘即为
证明
易得引理若则
分别代入$\left\{ \begin{aligned} &p_1=a\times b\times c,q_1=y,r_1=\gcd(a,q_1) \\ &p_2=b\times c,q_2=\frac{q_1}{r_1},r_2=\gcd(b,q_2) \\ &p_3=c,q_3=\frac{q_2}{r_2},r_3=\gcd(c,q_3) \end{aligned} \right. $即可
实现
我们发现实现的难点在于如何在时间内进行分解,询问部分较为容易
分解
对于,找到的最小质因子以及的合法分解且,的一种合法分解即为的升序排序
考虑证明:
- 时显然成立,分解为
- 当时将带入有
- 考虑的情况, ,显然有; ,由于不是素数,的最小质因子即为的第二小质因子,一定,而都为的非因子,有,与相矛盾,故不存在此情况
所以只用跑一次线性筛,用最小质因子更新即可,然后预处理出的数组
代码如下
// fac为合法分解,isp表示是否非质数,pri为质数数组,tot为pri的大小,pre为预处理的gcd数组,M为值域,T为sqrt(M) void work() { fac[1][0] = fac[1][1] = fac[1][2] = 1; for (int i = 2; i <= M; ++i) { if (!isp[i]) { fac[i][0] = fac[i][1] = 1; fac[i][2] = i; pri[++tot] = i; } for (int j = 1; pri[j] * i <= M; ++j) { int tmp = pri[j] * i; isp[tmp] = true; fac[tmp][0] = fac[i][0] * pri[j]; fac[tmp][1] = fac[i][1]; fac[tmp][2] = fac[i][2]; if (fac[tmp][0] > fac[tmp][1]) { fac[tmp][0] ^= fac[tmp][1] ^= fac[tmp][0] ^= fac[tmp][1]; } if (fac[tmp][1] > fac[tmp][2]) { fac[tmp][1] ^= fac[tmp][2] ^= fac[tmp][1] ^= fac[tmp][2]; } // 对于整数运算a ^= b ^= a ^= b等价于swap(a, b)这里就是手动进行length = 3的排序 if (i % pri[j] == 0) { break; } } } for (int i = 0; i <= T; ++i) { pre[0][i] = pre[i][0] = i; } for (int i = 1; i <= T; ++i) { for (int j = 1; j <= i; ++j) { pre[i][j] = pre[j][i] = pre[j][i % j]; } } }查询
若当前枚举的为,只需注意时分和两种情况即可
以下为代码
int gcd(int a, int b) { // 不想写if-else所以用三目运算符代替并缩进了一下 int ans = 1; for (int i = 0; i < 3; ++i) { int tmp = (fac[a][i] > T) ? (b % fac[a][i] ? 1 : fac[a][i] ) : pre[fac[a][i]][b % fac[a][i]]; b /= tmp; ans *= tmp; } return ans; }本题做法
基本上已经没了,就是求然后按题目给的算,放个代码吧
const int N = 5000; const int M = 1000000; const int T = 1000; const int Mod = 998244353; void work(); int gcd(int, int); int pre[T + 2][T + 2]; int a[N + 2], b[N + 2]; int fac[M + 2][3]; bool isp[M + 2]; int pri[M / 10], tot; int n; int main () { work(); read(n); for (int i = 1; i <= n; ++i) { read(a[i]); } for (int i = 1; i <= n; ++i) { read(b[i]); } for (int i = 1; i <= n; ++i) { int ans = 0; for (int j = 1, now = i; j <= n; ++j, now = now * 1ll * i % Mod) { ans = (ans + int(now * 1ll * gcd(a[i], b[j]) % Mod)) % Mod; } write(ans), EL; } return 0; } void work() { fac[1][0] = fac[1][1] = fac[1][2] = 1; for (int i = 2; i <= M; ++i) { if (!isp[i]) { fac[i][0] = fac[i][1] = 1; fac[i][2] = i; pri[++tot] = i; } for (int j = 1; pri[j] * i <= M; ++j) { int tmp = pri[j] * i; isp[tmp] = true; fac[tmp][0] = fac[i][0] * pri[j]; fac[tmp][1] = fac[i][1]; fac[tmp][2] = fac[i][2]; if (fac[tmp][0] > fac[tmp][1]) { fac[tmp][0] ^= fac[tmp][1] ^= fac[tmp][0] ^= fac[tmp][1]; } if (fac[tmp][1] > fac[tmp][2]) { fac[tmp][1] ^= fac[tmp][2] ^= fac[tmp][1] ^= fac[tmp][2]; } if (i % pri[j] == 0) { break; } } } for (int i = 0; i <= T; ++i) { pre[0][i] = pre[i][0] = i; } for (int i = 1; i <= T; ++i) { for (int j = 1; j <= i; ++j) { pre[i][j] = pre[j][i] = pre[j][i % j]; } } } int gcd(int a, int b) { int ans = 1; for (int i = 0; i < 3; ++i) { int tmp = (fac[a][i] > T) ? (b % fac[a][i] ? 1 : fac[a][i] ) : pre[fac[a][i]][b % fac[a][i]]; b /= tmp; ans *= tmp; } return ans; }
- 1
信息
- ID
- 4411
- 时间
- 1200ms
- 内存
- 32MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者