[CF938D] Buy a Ticket

题目

原题地址

解说

先说一下,洛谷上的中文翻译是有问题的,路程是往返的,汉语翻译没体现这一点,看一下原题里面的公式就行了。
这道题和之前我自己出的02的爱恋有些许相似,都需要把点权转化为边权。怎么转化呢?由于只有在终点处需要加上点权,所以我们自然地想到超级源点,从超级源点到每个点都连一条权值为点权的单向边保证只走一次,之后跑一次超级源点的单元最短路就行了。当然这就暗含了另一种思维方法:逆向。题目原意是只加终点的点权,但由于道路双向,我们从终点开始跑也是一样的,因此可以把终点当做起点跑最短路。

代码

#include
using namespace std;
typedef long long ll;
const int maxn=200000+5;
int n,m,tot;
int head[maxn];
ll dis[maxn];
struct edge{
	int to,next;
	ll w;
}e[3*maxn];
struct node{
	int num;ll dis;
	node(int x,ll y){
		num=x;
		dis=y;
	}
	bool operator<(const node &a)const{
		return dis>a.dis;
	}
};
void add(int a,int b,ll w){
	e[tot].to=b;
	e[tot].w=w;
	e[tot].next=head[a];
	head[a]=tot;
	tot++;
}
void Dijs(){
	priority_queue q;
	bool f[maxn];
	memset(f,0,sizeof(f));
	for (int i=1;i<=n;i++) dis[i]=4611686018427387903;
	dis[0]=0;
	q.push(node(0,0));
	while(!q.empty()){
		node p=q.top();q.pop();
		int k=p.num;
		if(f[k]) continue;
		f[k]=1;
		for (int i=head[k];i;i=e[i].next){
			int u=e[i].to;ll len;
			len=dis[k]+e[i].w;
			if(dis[u]>len){
				dis[u]=len;
				q.push(node(u,len));
			}
		}
	}
}
int main(){
	tot=1;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v; ll w;
		scanf("%d%d%lld",&u,&v,&w);
		add(u,v,2*w);//每条边要走两遍
		add(v,u,2*w);
	}
	for(int i=1;i<=n;i++){
		ll w;
		scanf("%lld",&w);
		add(0,i,w);//超级源点编号为0
	}
	Dijs();
	for(int i=1;i<=n;i++) printf("%lld ",dis[i]);
	return 0;
}

幸甚至哉,歌以咏志。

你可能感兴趣的:([CF938D] Buy a Ticket)