Loading

[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)\) ,因为这两条边可以免费走。【边五】会讲如果不同怎么拉边。

【边四】正向边向反向边拉边,因为走过去再走回来也可以。

我感觉剩下这种边是这种建图方法唯一的难点。

【边五】我们现在还没有处理颜色转换的边。这个东西看着贼难搞,不同颜色暴力拉边的话复杂度就又可以被卡成平方了。

换个思路,新建一个虚点,对于 \(u\) 出发的每种颜色,随便取一个边对应的点往虚点连长度为 \(1\) 的边,这个虚点往那个点连长度为 \(0\) 的边。

往虚点走就意味着要换颜色,否则根本不会走长度为 \(1\) 的边。而每一个颜色内部是可以随便走的,所以这种建图方法是对的。

总点数为 \(2m+n+2\) ,正向边和反向边总共 \(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;
}

posted @ 2020-12-14 18:34  zzctommy  阅读(124)  评论(0编辑  收藏  举报