[ARC061E]Snuke’s Subway Trip 题解
题目链接:[ARC061E]Snuke’s Subway Trip
题意:
Snuke的城镇有地铁行驶,地铁线路图包括 $N$ 个站点和 $M$ 个地铁线。站点被从 $1$ 到 $N$ 的整数所标记,每条线路被一个公司所拥有,并且每个公司用彼此不同的整数来表示。
第 $i$ 条线路( $1≤i≤M$ )是直接连接 $p_i$ 与 $q_i$ 的双向铁路,中间不存在其他站点,且这条铁路由 $c_i$ 公司所拥有。
如果乘客只乘坐同一公司的铁路,他只需要花费一元,但如果更换其他公司的铁路需要再花一元。当然,如果你要再换回原来的公司,你还是要花一元。
Snuke在 $1$ 号站的位置出发,他想通过地铁去第 $N$ 站,请求出最小钱数。如果无法到达第 $N$ 站,输出 $-1$。
输入格式:
第一行,输入 $N,M$
接下来的 $M$ 行,输入 $p_i$ $q_i$ $c_i$ ,代表 $p_i$ 与 $q_i$ 中间有直接连接的双向边,且这条铁路由 $c_i$ 公司所拥有。
输出格式:
输出最小钱数(若无法到达,输出 $-1$ )
数据范围:
$2\le N \le 10^5,~0\le M\le 2×10^5,~1\le p_i,q_i\le N,~1\ \leq\ c_i\ \leq\ 10^6,~p_i \ne q_i$
容易发现,由相同颜色边连接的一个联通块,是可以在其中自由穿行而没有花费的
如果我们把这样的联通块合并成一个点,那么原题就可以用 01bfs 解决了。
怎么合并这些块呢?我们可以建一个虚点 $u_0$ ,让联通块里的所有点 $v$ 连边 $(u_0,v,1)$ 和 $(v,u,0)$
具体方法就是枚举所有颜色的边,把这些边涉及的点拿出来,再用并查集合并找联通块数量,最后连边。
时间复杂度 $\mathcal{O}(n)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
typedef pair<int,int> pii;
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(2e5 + 15))
const int C = 1e6;
int n,m,tot,fa[N],col[N],d[N * 2];
vector<pii> e[C + 15], g[N * 2];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void addEdge(int u,int v) {
g[u].emplace_back(v, 0); g[v].emplace_back(u, 1);
}
void solve()
{
deque<int> q; q.push_back(1);
memset(d, -1, sizeof(d)); d[1] = 0;
while(!q.empty()) {
int u = q.front(); q.pop_front();
for(auto [v, w] : g[u]) if(d[v] == -1)
{
d[v] = d[u] + w;
if(w == 1) q.push_back(v); else q.push_front(v);
}
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> m; tot = n;
for(int i = 1,u,v,c; i <= m; i++)
{
cin >> u >> v >> c;
e[c].emplace_back(u, v);
}
for(int i = 1; i <= C; i++) if(!e[i].empty())
{
vector<int> p;
for(auto [x, y] : e[i]) { p.emplace_back(x); p.emplace_back(y); }
sort(p.begin(), p.end());
p.erase(unique(p.begin(), p.end()), p.end());
for(int x : p) fa[x] = x;
for(auto [x, y] : e[i]) fa[find(x)] = find(y);
for(int x : p) if(find(x) == x) col[x] = ++tot;
for(int x : p) addEdge(x, col[find(x)]);
}
solve(); cout << d[n] << '\n';
return 0;
}
参考文献: