[BZOJ4454][C Language Practice][GCD]
题目大意:
思路:
如果对于每两个数都求一次gcd显然会超时。
考虑O(N)预处理,O(1)求gcd。
不妨设m=n√,可以在O(m2)也就是O(N)的时间里求出m范围内的gcd(辗转相除递推)
m 范围外的数X,一定可以分解成A∗B∗C 的形式,其中A,B,C要么<=m,要么是一个素数。分解素数可以线性筛预处理出每个数的最小质因数d,然后
AX,BX,CX 可以由AX/d,BX/d,CX/d转移得到Hint
数组要开的小心,因为内存范围限制得很紧
大部分范围用int就可以,除了答案要用unsigned
读入优化的缓存数组开太大会炸掉
代码:
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 1000010;
inline void read(int &x) {
scanf("%d", &x);
}
int g[1010][1010], p[Maxn / 10], tot, d[Maxn], n, m, A[Maxn], B[Maxn], C[Maxn];
bool vis[Maxn];
inline void init(int n) {
for (int i = 0; i <= 1005; i++)
for (int j = 0; j <= i; j++) {
if (!i || !j) g[i][j] = i + j;
else g[i][j] = g[j][i % j];
}
for (int i = 0; i <= 1005; i++)
for (int j = i + 1; j <= 1005; j++) g[i][j] = g[j][i];
d[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
p[++tot] = i;
d[i] = i;
}
for (int j = 1; j <= tot; j++) {
if (i * p[j] > n) break;
vis[i * p[j]] = 1;
if (i % p[j] == 0) {
d[i * p[j]] = d[i];
break;
}
d[i * p[j]] = p[j];
}
}
A[1] = B[1] = C[1] = 1;
for (int i = 2; i <= n; i++) {
int j = i / d[i];
A[i] = A[j], B[i] = B[j], C[i] = C[j];
if (A[i] * d[i] <= 1000) A[i] *= d[i];
else if (B[i] * d[i] <= 1000) B[i] *= d[i];
else C[i] *= d[i];
}
}
int X[3], c;
inline int gcd(int a, int b) {
if (!a || !b) return a + b;
if (a <= 1000 && b <= 1000) return g[a][b];
c = 0; int ans = 1, d = 1;
if (A[a] != 1) X[c++] = A[a];
if (B[a] != 1) X[c++] = B[a];
if (C[a] != 1) X[c++] = C[a];
for (int i = 0; i < c; i++) {
if (X[i] <= 1000) d = g[X[i]][b % X[i]];
else if (b % X[i] == 0) d = X[i];
else d = 1;
ans *= d; b /= d;
}
return ans;
}
int T, a[2010], b[2010];
int main(void) {
//freopen("in.txt", "r", stdin);
init(1000000);
read(T);
while (T--) {
read(n), read(m);
for (int i = 0; i < n; i++) read(a[i]);
for (int i = 0; i < m; i++) read(b[i]);
unsigned int ans = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
ans += gcd(a[i], b[j]) ^ i ^ j;
printf("%u\n", ans);
}
return 0;
}
完。
By g1n0st