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

D. Friendly Spiders

题目大意:

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


解题思路:

因为任意两个点之间有无向边当且仅当gcd(ai,aj)!=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 @   empty_y  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示