Codeforces Round #843 (Div. 2)D(图论+deque优化)
D. Friendly Spiders
题目大意:
存在n(n<=3e5)个点,每个点都有一个点权a,任意两个点之间有无向边当且仅当gcd(a,a)!=1
现在给定起点a,求到终点b的最短路径并输出具体路径(所有边的边权为1)
解题思路:
因为任意两个点之间有无向边当且仅当gcd(a,a)!=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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】