AtCoder Regular Contest 061
C,D 太简单,懒得写题解。
F 题数数题,还是个黑题,非常恐怖,先溜了 QAQ,有兴趣可以看 command_block 的题解。
E - Snuke's Subway Trip
\(n\) 个点,\(m\) 条边的无向图,每条边 \((u,v)\) 有一个标记 \(t\)。
从 \(1\) 走到 \(n\),如果第 \(i+1\) 步走的边和第 \(i\) 步走的边标记不同,则代价 \(+1\)。
询问从 \(1\) 走到 \(n\) 的最小代价。
\(1\le n\le 10^5,0\le m\le 2\times 10^5,1\le t\le 10^6\)
看完题目我产生了一个比较离谱的想法:把标号相同的边构成的连通块操作一下,后来感觉时间复杂度太高就润了,结果正解居然真是这样做。
(其实这题解法相当多,有兴趣可以去洛谷题解看看)
沿着上述思路,将所有连通块内的点两两连边,跑最短路即可。
然鹅想想就知道不太可能,因为这样做边数是 \(\frac{n\times (n-1)}{2}\) 级别的。
但是这并不意味着这种做法是错误的,我们通过巧妙的建图回避掉巨大的边数。
对于每一个连通块,建立一个虚点 \(p\),将连通块内的所有点与 \(p\) 连一条权值为 \(0.5\) 的边。
不难发现,这两种建图完全等价,所以直接在这张图上跑最短路即可。
为了方便处理,可以先将边权设为 1,跑完最短路再除以 2 即可。
因为我用了并查集求连通块,所以时间复杂度为 \(\mathcal O(n+m\log n)\)
Code
// Problem: E - Snuke's Subway Trip
// Contest: AtCoder - AtCoder Regular Contest 061
// URL: https://atcoder.jp/contests/arc061/tasks/arc061_c
// Memory Limit: 256 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
using pii = std::pair<int,int>;
const int maxn = 3e5 + 5;
const int maxm = 1e6 + 5;
std::vector<pii> s[maxm];
std::vector<int> res,G[maxn];
int n,m,tot,dis[maxn],pre[maxn],anton[maxn];
bool vis[maxn];
int find(int x) {
return x == pre[x] ? x : pre[x] = find(pre[x]);
}
int main() {
scanf("%d %d",&n,&m);
int cnt = 0;
for(int i = 1;i <= m;++ i) {
int u,v,t;
scanf("%d %d %d",&u,&v,&t);
s[t].pb(u , v);
cnt = std::max(cnt , t);
}
for(int i = 1;i <= n + m;++ i)
pre[i] = i;
tot = n;
for(int i = 1;i <= cnt;++ i) {
if(s[i].empty())continue ;
res.clear();
for(auto [u , v] : s[i]) {
res.pb(u);
res.pb(v);
if(find(u) == find(v))continue ;
pre[find(v)] = find(u);
}
for(auto& v : res) {
if(vis[v])continue ;
if(!anton[find(v)])
anton[find(v)] = ++ tot;
vis[v] = true;
G[v].pb(anton[find(v)]);
G[anton[find(v)]].pb(v);
}
for(auto& v : res)anton[find(v)] = 0,vis[v] = false;
for(auto& v : res)pre[v] = v;
}
std::queue<int> q;
memset(dis , 0x3f , sizeof(dis));
memset(vis , false , sizeof(vis));
vis[1] = true;
dis[1] = 0;
q.emplace(1);
while(!q.empty()) {
int u = q.front();
q.pop();
for(auto& v : G[u]) {
if(vis[v])continue ;
vis[v] = true;
dis[v] = dis[u] + 1;
q.emplace(v);
}
}
printf("%d\n",dis[n] == 0x3f3f3f3f ? -1 : dis[n] / 2);
return 0;
}