「JOI 2021 Final」机器人

「JOI 2021 Final」机器人

原问题中颜色什么时候改没有影响

显然不能记录每条边的颜色,显然在行走过程中不会回到原先的点

因此考虑简化状态

从一个点出去时,走了一条边((u,v,c,w)),从(u)出发颜色为(c)的边(w)之和为(S_{u,c}),则有两种转移:

1.走过来时的边被改变了,权值(w)

2.改变了其他同种颜色的边,权值(S_{u,c}-w)

对于2情况在前面修改的边,在后面不会产生贡献(否则可以直接通过这条边过去,而不需要绕路)

可以直接转移到对应节点

1类转移的贡献相当于下次走这种颜色时,可以少改变一条边的颜色

即下一次转移2时,权值变为(S_{v,c}-w-w')

可以增加一个额外权值(-w),然后对于下一个点(v)所有颜色为(c)的出边转移,这可以通过构建虚点解决

实际上总权值就是(0)

考虑到一种不合法的情况,即走回(u),但是这样的转移权值就是(S_{v,c}-w'ge 0)

非法情况无影响

最终得到的转移就是一个非负边权的最短路图,可以跑(Dijkstra)解决

总点数(leq n+2m),总边数(leq 6m)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)

char IO;
int rd(){
	int s=0;
	while(!isdigit(IO=getchar()));
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return s;
}

const int N=5e5+10,INF=1e9+10;

int n,m,k;
struct Edge{
	int v,c,w;
};
vector <Edge> G[N];
int U[N],V[N],C[N],W[N];

struct Node{
	int u; ll d;
	bool operator < (const Node __) const {
		return d>__.d;
	}
};
priority_queue <Node> que;
ll dis[N],S[N];
void Upd(int u,ll d){
	if(dis[u]<=d) return;
	dis[u]=d,que.push((Node){u,d});
}
map <int,int> st[N];
void AddEdge(int u,int v,int c,int w){
	if(!st[u].count(c)) {
		st[u][c]=++k;
		G[u].pb((Edge){k,0,0});
	}
	int t=st[u][c];
	G[t].pb((Edge){v,c,w}),S[t]+=w;
}

void Dijkstra(){
	memset(dis,63,sizeof dis);
	dis[1]=0,que.push((Node){1,0});
	while(!que.empty()) {
		int u=que.top().u; ll d=que.top().d; que.pop();
		if(dis[u]<d) continue;
		if(u<=n) {
			for(Edge dd:G[u]) {
				int t=dd.v;
				for(Edge i:G[t]) {
					Upd(i.v,d+min((ll)i.w,S[t]-i.w));
					Upd(st[i.v][i.c],d);
				}
			}
		} else for(Edge i:G[u]) Upd(i.v,d+S[u]-i.w);
	}
	printf("%lld
",dis[n]<1e17?dis[n]:-1);
}

int main(){
	freopen("robot.in","r",stdin),freopen("robot.out","w",stdout);
	n=rd(),m=rd(),k=n;
	rep(i,1,m) {
		int u=rd(),v=rd(),c=rd(),w=rd();
		AddEdge(u,v,c,w),AddEdge(v,u,c,w);
	}
	Dijkstra();
}
原文地址:https://www.cnblogs.com/chasedeath/p/14436028.html