arc061 E - Snuke‘s Subway Trip

E - Snuke's Subway Trip

考察建图的一个最短路问题,因为切换颜色会导致cost+1,因此可以考虑如下建图,对于某一种颜色边构成的一个连通块,新开一个点t,每个连通块内点向t连一条代价为1的边,然后t向连通块内点连一条代价为0的边,此时就可以发现选择走某种颜色就是能用1的代价到达该色的连通块内每个点,之后跑0/1bfs即可。

#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define sc second
#define pb push_back
#define ll long long
#define trav(v,x) for(auto v:x)
#define all(x) (x).begin(), (x).end()
#define VI vector<int>
#define VLL vector<ll>
#define pll pair<ll, ll>
#define double long double
//#define int long long
using namespace std;
const int N = 1e6 + 100;
const int inf = 1e9;
//const ll inf = 1e18;
const ll mod = 998244353;//1e9 + 7;

#ifdef LOCAL
void debug_out(){cerr << endl;}
template<typename Head, typename... Tail>
void debug_out(Head H, Tail... T)
{
	cerr << " " << to_string(H);
	debug_out(T...);
}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

void sol()
{
	int n, m, num;
	cin >> n >> m;
	num = n;
	vector<tuple<int, int, int>> edge;
	for(int i = 0; i < m; i++)
	{
		int x, y, c;
		cin >> x >> y >> c;
		--x, --y;
		edge.pb(make_tuple(c, x, y));
	}
	sort(all(edge));
	VI fa(n), sz(n);
	vector<VI> son(n);
	function<int(int)> find = [&](int x)
	{
		return fa[x] == x ? x : find(fa[x]);
	};
	VI cur;
	vector<vector<pii>> adj(N);
	function<void(int)> dfs = [&](int x)
	{
		cur.pb(x);
		trav(v, son[x])
			dfs(v);
	};
	auto gao = [&](int x)
	{
		cur.clear();
		dfs(x);
		int nw = num++;
		//cerr << "GG" << '\n';
		trav(v, cur)
		{
			//cerr << v << '\n';
			adj[v].pb(pii(0, nw));
			adj[nw].pb(pii(1, v));
		}
		//cerr << "FIN" << '\n';
	};
	for(int l = 0, r; l < m; l = r)
	{
		r = l;
		while(r < m && get<0>(edge[r]) == get<0>(edge[l]))
			++r;
		for(int i = l; i < r; i++)
		{
			int c, x, y;
			tie(c, x, y) = edge[i];
			//cerr << "!!" << c << ' ' << x << ' ' << y << '\n';
			fa[x] = x;
			fa[y] = y;
			son[x].clear();
			son[y].clear();
			sz[x] = sz[y] = 1;
		}
		for(int i = l; i < r; i++)
		{
			int c, x, y;
			tie(c, x, y) = edge[i];
			int fx = find(x), fy = find(y);
			if(fx == fy)
				continue;
			if(sz[fx] < sz[fy])
				fa[fx] = fy, sz[fy] += sz[fx], son[fy].pb(fx);
			else fa[fy] = fx, sz[fx] += sz[fy], son[fx].pb(fy);
		}
		for(int i = l; i < r; i++)
		{
			int c, x, y;
			tie(c, x, y) = edge[i];
			if(fa[x] == x)
				gao(x), fa[x] = -1;
			if(fa[y] == y)
				gao(y), fa[y] = -1;
		}
	}
	//cerr << "SSSS" << '\n';
	vector<int> dis(num, inf), vis(num, 0);
	deque<int> q;
	dis[0] = 0;
	q.push_back(0);
	while(!q.empty())
	{
		int nw = q.front();
		q.pop_front();
		if(vis[nw])
			continue;
		vis[nw] = 1;
		sort(all(adj[nw]));
		trav(v, adj[nw])
		{
			int to, d;
			tie(d, to) = v;
			if(vis[to])
				continue;
			if(dis[to] > dis[nw])
			{
				//cerr << nw << ' ' << to << ' ' << d << '\n';
				dis[to] = dis[nw] + d;
				if(d == 0)
					q.push_front(to);
				else q.push_back(to);
			}
		}
	}
	if(dis[n - 1] == inf)
		dis[n - 1] = -1;
	cout << dis[n - 1] << '\n';
}

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
//	int tt;
//	cin >> tt;
//	while(tt--)
		sol();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值