2025.03.26 CW 模拟赛 A. 公约数神庙
题解链接.
A. 公约数神庙
题目描述
当大地陷入了混乱和分裂,一位智者带来了一本神秘的古老书籍。这本书上写着关于 \(n\) 个古老神庙的秘密,每座神庙都藏有珍贵的宝物。这些神庙被分布在各种不同的地方,被认为是人类文明的遗产。
第 \(i\) 座神庙有一个独特的权值 \(a[i]\),代表着其中蕴含的智慧和力量。这些神庙之间有着一种神秘的联系:若 \(i \leq j\) 且 \(\gcd(a[i], a[j]) > 1\),那么你可以从神庙 \(i\) 走到神庙 \(j\)。额外约定:
- 神庙 \(i\) 能走到神庙 \(i\)
- \(\gcd(0, 0) = 0\)
现在,你面临 \(q\) 个询问 \((x, y)\),你希望知道是否存在一条路径可以从神庙 \(x\) 走到神庙 \(y\),从而传递它们的智慧和力量。你的探险将成为人们口中的传奇,这个世界将因你的行动而改变!
思路
先特判掉一些一定无解或有解的情况:
- \(x = y\), 此时一定有解.
- \(a_x = 1 \or a_y = 1\), 此时一定无解.
- \(a_x = 0 \and a_y = 1\), 若 \(\nexists i \in (x, y), a_i > 1\), 那么一定无解, 否则有解.
我们令 \(k\) 为 \(1000\) 以内的素数个数, 考虑对于每一个素数, 我们建一条链 \(l_i\). 同时, 如果我们能够走到 \(l_{i, j}\), 那么我们就一定可以走到 \(l_{i, k > j}\).
通过打表可以发现在 \(1000\) 以内只有 \(k = 168\) 个素数, 同时又因为 \(2 \times 3 \times 5 \times 7 \times 11 > 1000\), 所以 \(1000\) 以内的数最多只会有 \(4\) 个质因子.
于是我们就可以在 \(\mathcal{O}(nk)\) 的时间复杂度内建出所有链. 定义 \(f_{i, j}\) 表示从 \(i\) 出发, 在 \(j\) 链上能走到的最近位置. 在对 \(f_{i, j}\) 进行转移时, 我们进行分类讨论:
- \(j \mid a_i\), 那么 \(f_{i, j} = i\).
- \(j \nmid a_i\), 由于 \(a_i\) 的出边最多只有 \(4\) 条, 我们暴力枚举并转移即可.
写成转移式子就是
其中 \(i \to k\) 指的就是 \(a_i\) 的出边.
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
int read() {
int x = 0; char c = getchar();
while (c < '0' or c > '9') {
c = getchar();
}
while (c >= '0' and c <= '9') {
x = x * 10 + (c & 15);
c = getchar();
}
return x;
}
constexpr int N = 100001;
constexpr int M = 1001, K = 169, V = 1000;
int n, q, a[N], pre[N], f[N][K], lst[K];
int prime[K], cnt;
bool is_prime[M];
vector<int> v[M];
void init() {
memset(f, 63, sizeof f);
for (int i = 2; i <= V; ++i) {
if (!is_prime[i]) {
prime[++cnt] = i;
v[0].push_back(i);
}
for (int j = 1; i * prime[j] <= V and j <= cnt; ++j) {
is_prime[i * prime[j]] = true;
if (!(i % prime[j])) {
break;
}
}
}
n = read(), q = read();
for (int i = 1; i <= n; ++i) {
a[i] = read();
pre[i] = pre[i - 1] + (a[i] > 1);
if (a[i] <= 1 or !v[a[i]].empty()) {
continue;
}
int t = a[i];
for (int j = 1; j <= cnt and prime[j] <= t; ++j) {
if (!(t % prime[j])) {
v[a[i]].push_back(j);
while (!(t % prime[j])) {
t /= prime[j];
}
}
}
}
}
void calculate() {
for (int i = n; i; --i) {
if (!a[i]) {
for (int j = 1; j <= cnt; ++j) {
f[i][j] = lst[j] = i;
}
continue;
}
for (int x : v[a[i]]) {
for (int j = 1; j <= cnt; ++j) {
f[i][j] = min(f[i][j], f[lst[x]][j]);
}
f[i][x] = lst[x] = i;
}
}
for (int x, y; q--; ) {
x = read(), y = read();
if (x == y) {
puts("Shi");
continue;
}
if (a[x] == 1 or a[y] == 1) {
puts("Fou");
continue;
}
if (!a[x] and !a[y]) {
puts((pre[y - 1] - pre[x] > 0) ? "Shi" : "Fou");
continue;
}
bool flag = false;
for (int i : v[a[y]]) {
flag |= f[x][i] <= y;
}
puts(flag ? "Shi" : "Fou");
}
}
void solve() {
init();
calculate();
}
int main() {
solve();
return 0;
}

浙公网安备 33010602011771号