Loading

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;
}
posted @ 2022-10-31 16:37  Skylakes  阅读(30)  评论(0编辑  收藏  举报