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\) 条, 我们暴力枚举并转移即可.

写成转移式子就是

\[f_{i, j} = \begin{cases} i & j \mid a_i \\ \min_{i \to k} f_{k, j} & j \nmid a_i \end{cases} \]

其中 \(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;
}
posted @ 2025-03-26 21:07  Steven1013  阅读(86)  评论(0)    收藏  举报