P5060 旅行 题解

题目传送门

思路

由于题面中要求最后的便签是 p p p 的倍数,这是一个非常经典的分层图题。

将每个点拆分成 p p p 个点。为了方便,我们设点 ( i , j ) (i,j) (i,j) 表示原图中点 i i i 的第 j j j 层,即到点 i i i 时剩下便签数对 p p p 取模为 j j j

考虑建图。对于原图中的每一条边 ( u , v , w ) (u,v,w) (u,v,w),在分层图上建边时,枚举 u u u 的层数 j j j,那么到了 v v v 的时候层数就变成了 ( j + w )   m o d   p (j+w)\bmod p (j+w)modp。所以连接一条从 ( u , j ) (u,j) (u,j) ( v , ( j + w )   m o d   p ) (v,(j+w)\bmod p) (v,(j+w)modp),权值为 w w w 的边。

问题就被转化成了求 ( s , 0 ) (s,0) (s,0) ( t , 0 ) (t,0) (t,0) 的最短路。跑一遍 Dijkstra 即可。

实测堆优化开 O2 才能过。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;

template<typename T> inline void read(T &x)
{
	x = 0;
	T f = 1;char ch = getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
		{
			f = -1,ch = getchar();
			break;
		}
		ch = getchar();
	}
	while(ch>='0'&&ch<='9')
		x = (x<<3)+(x<<1)+ch-48,ch = getchar();
	x*=f;
}
template<typename T> inline T read()
{
	T x;read(x);return x;
}
template<typename T> void write(T x)
{
    if(x<0) x = -x,putchar('-');
    if(x>9) write(x/10);
    putchar(x%10+48);
}
template<typename T> inline void writen(T x)
{
    write(x);
    putchar(10);
}
const int N = 5e4+5,M = 2e5+5;
int n,m,p,s,t,cnt,head[N*50],to[M*50],nxt[M*50];
ll g[M*50];
inline void add(int x,int y,int z)
{
	nxt[++cnt] = head[x];
	head[x] = cnt;
	to[cnt] = y,g[cnt] = z;
}
inline int id(int x,int y){return (x-1)*p+y+1;}//(i,j) 的编号 
inline int get(int x){return (x-1)/p+1;}//(i,j) 编号对应的 i 
ll dis[N*50];
int pre[N*50];
bool vis[N*50];
void print(int x)//递归输出路径 
{
	if(pre[x]) print(pre[x]),putchar('-'),putchar('>');
	write(get(x));
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	read(n),read(m),read(p),read(s),read(t);
	for(int i = 1,u,v,w;i<=m;i++)
	{
		read(u),read(v),read(w);
		for(int j = 0;j<p;j++)
			add(id(u,j),id(v,(j+w)%p),w);
	}
	priority_queue<pair<ll,int> > q;
	q.push({0,id(s,0)});
	for(int i = 1;i<=n*p;i++) dis[i] = 2e18;  
	dis[id(s,0)] = 0;
	while(!q.empty())//Dijkstra 
	{
		int u = q.top().second;q.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		for(int i = head[u];i;i = nxt[i])
		{
			int v = to[i];
			if(dis[v]>dis[u]+g[i])
				dis[v] = dis[u]+g[i],pre[v] = u,q.push({-dis[v],v});
		}
	}
	if(dis[id(t,0)]==2e18) return puts("jjc fails in travelling"),0;//不连通 
	writen(dis[id(t,0)]);
	print(id(t,0));
	return 0;
}
  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值