1. 程式人生 > 實用技巧 >[ARC061C] すぬけ君の地下鉄旅行 / Snuke's Subway Trip

[ARC061C] すぬけ君の地下鉄旅行 / Snuke's Subway Trip

ATCoder題目傳送門

洛谷中文翻譯傳送門

這題應該有好多種建圖方法吧?這種方法與其他題解都不一樣。

一個套路的建圖方法,做過這題就很容易想到。

感覺這種題解不是很好寫,那就直接寫怎麼建圖然後講每一種邊的意義吧。

【建點】把邊當成點,正向邊和反向邊分別建點,再建立超級源點 \(S\) ,超級匯點 \(T\)

對於每一個節點 \(u\) 按照 \(v\) 顏色的顏色從小到大排序(我習慣把邊設成 \(u\to v\))。這樣所有起點為 \(u\) ,終點顏色相同的點會相鄰。

【邊一】如果這條邊的終點是 \(n\) ,那麼拉邊 \((id,T,0)\) ,因為它可以直接連到 \(n\)

【邊二】如果這條邊的起點是 \(1\)

,那麼拉邊 \((S,id,1)\),因為它從 \(S\) 出發就已經相當於“換”了一次顏色。

【邊三】對於 \(u\) 相同的兩個相鄰的出邊終點 \(v_1,v_2\) (邊的編號為 \(id_1,id_2\)),如果顏色相同,那麼拉邊 \((id_1,id_2,0),(id_2,id_1,0)\) ,因為這兩條邊可以免費走。【邊五】會講如果不同怎麼拉邊。

【邊四】正向邊向反向邊拉邊,因為走過去再走回來也可以。

我感覺剩下這種邊是這種建圖方法唯一的難點。

【邊五】我們現在還沒有處理顏色轉換的邊。這個東西看著賊難搞,不同顏色暴力拉邊的話複雜度就又可以被卡成平方了。

換個思路,新建一個虛點 \(x\)

,對於 \(u\) 出發的每種顏色,隨便取一個邊對應的點往虛點連長度為 \(1\) 的邊,這個虛點往那個點連長度為 \(0\) 的邊。

往虛點走就意味著要換顏色,否則根本不會走長度為 \(1\) 的邊。而每一個顏色內部是可以隨便走的,所以這種建圖方法是對的。

總點數為 \(2m+n\) ,不要開小。還有顏色值域上限是 \(10^6\) ,不要開小了。

可以發現邊權只有 \(0\)\(1\) ,直接bfs就可以 \(O(n)\) ,我偷懶寫了dij。

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mkp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define sz(v) (int)v.size()
typedef long long LL;
typedef double db;
template<class T>bool ckmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>bool ckmin(T&x,T y){return x>y?x=y,1:0;}
#define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
#define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f?x:-x;
}
const int N = 500005;
const int C = 1000005;
const int inf=0x3f3f3f3f;
int n, m, S, T, tot;
int dis[N];
bool vis[N];
struct node{
	int w, v, id;
	node(){w = v = id = 0;}
	node(int w_, int v_, int id_) {w = w_, v = v_, id = id_;}
	inline bool operator < (const node&t) const {return w < t.w;}
};
vector <node> E[N];
vector <pair<int, int> > e[N];
void Dij(){
	priority_queue <pair<int,int> > pq;
	memset(dis, 0x3f, sizeof(dis));
	pq.push(mkp(dis[S]=0, S));
	while (!pq.empty()){
		int u = pq.top().se; pq.pop();
		if (vis[u]) continue;
		vis[u] = 1;
		for (int i = 0, up = sz(e[u]); i < up; ++ i){
			int v = e[u][i].se;
			if (ckmin(dis[v] , dis[u] + e[u][i].fi))
				if(!vis[v]) pq.push(mkp(-dis[v], v));
		}
	}
}
signed main() {
	n = read(), m = read(), S = m * 2 + 2, T = tot = S + 1;
	rep(i, 1, m) {
		int x = read(), y = read(), z = read();
		E[x].pb(node(z, y, i << 1)),E[y].pb(node(z, x, i << 1 | 1));
	}
	rep(i, 1, n){
		static int tag[C];
		sort(E[i].begin(), E[i].end()), ++tot;
		for (int j = 0, up = sz(E[i]); j < up; ++ j){
			node now = E[i][j];
			if(tag[now.w]!=i){
				tag[now.w]=i;
				e[now.id].pb(mkp(1, tot));
				e[tot].pb(mkp(0, now.id));
			}
			e[now.id].pb(mkp(0,now.id ^ 1));
			if (i == 1) e[S].pb(mkp(1, now.id));
			if (now.v == n) e[now.id].pb(mkp(0, T));
			if (j && now.w == E[i][j-1].w) e[E[i][j-1].id].pb(mkp(0, now.id)), e[now.id].pb(mkp(0, E[i][j-1].id));
		}
	}
	Dij(), printf("%d\n", dis[T] == inf ? -1 : dis[T]);
	return 0;
}