Codeforces Round #843 (Div. 2)D(图论+deque优化)

D. Friendly Spiders

题目大意:

存在n(n<=3e5)个点,每个点都有一个点权a\(_i\),任意两个点之间有无向边当且仅当gcd(a\(_i\),a\(_j\))!=1
现在给定起点a,求到终点b的最短路径并输出具体路径(所有边的边权为1)


解题思路:

因为任意两个点之间有无向边当且仅当gcd(a\(_i\),a\(_j\))!=1,所以我们可以将其理解为,如果两个点的点权有公共质因数,则两个点之间有边,由此我们可以将每个点分解为质因数,质因数作为虚拟节点边权为0,然后连接节点及其质因数(minp[x]->x,x->minp[x]),通过虚拟节点的中转,我们可以到达所有拥有公共质因数的节点,然后用bfs搜索路径,用pre保存节点的前置节点即可。
不过由于n<=3e5,将其分解为质因数之后,再连点,会导致数据量扩大,需要进行剪枝防止重复搜索(多个数的质因数相同,所以在搜索质因数的时候有可能导致质因数所连的节点被重复搜索)所以我们需要对较少的质因数优先进行搜索,对实际节点后搜索,但是普通的队列不能满足这样的需求,这时就需要用deque来优化,每次入队时判断一下是否为实际节点,如果是实际节点,就放到队尾,如果是虚拟节点,就放到队头,这样就能保证质因数优先搜索

扩展域并查集思想优化建图

对于区分虚拟节点和实际节点,我们其实可以运用类似于扩展域并查集的思想,如果是质因数,他的pos就应该是primes[j]+n,否则对于实际节点他的pos就是i,所以在判断时如果pos<=n就放入路径中


代码实现:

#include<bits/stdc++.h>
using namespace std;
# define int long long
# define endl "\n"
# define mk(a,b) make_pair(a,b)
const int N = 3e5 + 10, inf = 1e18;
int primes[N];//质数
int vis[N], minp[N], cnt; //minp 对x的最小质数
void init() {
	for (int i = 2; i < N; i ++ ) {
		if (!vis[i]) primes[cnt ++ ] = i, minp[i] = i;
		for (int j = 0; primes[j] * i < N; j ++ ) {
			vis[primes[j] * i] = true;
			minp[primes[j] * i] = primes[j];
			if (i % primes[j] == 0) break;
		}
	}
}
vector<pair<int, int>> e[2 * N];
int pre[N * 2];
int ok[2 * N];
int k[N];
void solve() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		int x;
		cin >> x;
		k[i] = x;
		while (x != 1) {
			int tmp = minp[x];
			e[k[i]].push_back(mk(tmp, tmp + n));
			e[tmp].push_back(mk(k[i], i));
			while (x % tmp == 0) x /= tmp;
		}
	}

	int a, b;
	cin >> a >> b;
	if (a == b) {
		cout << 1 << endl;
		cout << a << endl;
		return;
	}
	if (k[a] == 1 || k[b] == 1) {
		cout << -1 << endl;
		return;
	}
	if (k[a] == k[b]) {
		cout << 2 << endl;
		cout << a << " " << b << endl;
		return;
	}
	deque<pair<int, int>> q;
	q.push_back(mk(k[a], a));
	ok[a] = 1;

	while (q.size()) {
		auto [now, pos] = q.front();
		q.pop_front();
		if (pos == b) {
			break;
		}
		bool flag = false;
		for (auto [v, p] : e[now]) {
			if (ok[p]) continue;
			ok[p] = 1;
			if (p <= n)
				q.push_back(mk(v, p));
			else q.push_front(mk(v, p));
			pre[p] = pos;
			if (p == b) {
				flag = true;
				break;
			}
		}
		if (flag) break;
	}
	if (!ok[b]) {
		cout << -1 << endl;
		return;
	}
	vector<int> ans;
	while (b != a) {
		if (b <= n)
			ans.push_back(b);
		b = pre[b];
	}
	ans.push_back(a);
	cout << ans.size() << endl;
	for (int i = ans.size() - 1; i >= 0; --i) cout << ans[i] << " ";
	cout << endl;
}
int tt;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	tt = 1;
	init();
//	cin >> tt;
	while (tt--) solve();


	return 0;
}
posted @ 2023-01-11 13:45  empty_y  阅读(53)  评论(0)    收藏  举报