1 条题解
-
0
自动搬运
来自洛谷,原作者为

Genius_Star
跟你爆了搬运于
2025-08-24 22:51:29,当前版本为作者最后更新于2023-10-23 20:54:51,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题意:
给出一个有向图,当前在 号点,初始在时间 ,必须在 的倍数的时间出发,且到终点的时间也必须是 的倍数。
每条边有一个边权 ,只有在当前时间 时才可以通过,且不能在原地不动,即每一个时间点必须走一条边。
问从 号点出发到 号时最早的时刻。(没有方案则输出 )
思路:
因为 很小,所以我们可以从 入手。
注意到,如果我当前到达了 号点,且当前时间为 ,这条边边权为 ,如果 ,那么显然当前不能通过。
但是因为如果当前可以走到这个点,那么可以晚一些 的倍数的时间出发,依然可以走到这个点,则我们可以在入口处等待一些时间,使得可以通过这条边,等待时间为 ,即等待 个 的倍数,这样就可以通过这条边了,耗费时间为 。
现在通过每条边的时间更出发点为 的倍数有关系,则我们可以建立以下状态:定义 为到达 号点的时间 的值为 时的最短消耗时间。
那么答案显然是 。
然后看一下转移,如果 了,那么可以直接通过,则 $dis_{v,(p+1) \bmod k} \to \min(dis_{v,(p+1) \bmod k},p+1)$。
否则的话,令 ,即我们在入口处等待一些时间,使得可以走到这条边,转移为 $dis_{v,(t+1) \bmod k} \to \min(dis_{v,(t+1) \bmod k},t+1)$。
可以运用 dijkstra 算法的思想来进行转移,每次去堆顶选取耗时最短的那个点,然后逐层松弛。
时间复杂度为:。
完整代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll N=10010,M=105; inline ll read(){ ll x=0,f=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } inline void write(ll x){ if(x<0){ putchar('-'); x=-x; } if(x>9) write(x/10); putchar(x%10+'0'); } ll n,m,k; ll dis[N][M]; bool f[N][M]; vector<pair<ll,ll>> E[N]; priority_queue<pair<ll,ll>,vector<pair<ll,ll>>,greater<pair<ll,ll>>> q; void add(ll u,ll v,ll w){ E[u].push_back({v,w}); } void dijkstra(ll s){ dis[s][0]=0; q.push({0,s}); while(!q.empty()){ ll u=q.top().second,p=q.top().first; q.pop(); if(f[u][p%k]) continue; f[u][p%k]=1; for(auto d:E[u]){ ll v=d.first,w=d.second,t=(p+1)%k; if(p>=w) t=p; else t=((w-p+k-1)/k)*k+p; if(dis[v][(t+1)%k]>t+1){ dis[v][(t+1)%k]=t+1; q.push({t+1,v}); } } } } int main(){ memset(dis,0x3f,sizeof(dis)); n=read(),m=read(),k=read(); for(int u,v,w,i=0;i<m;i++){ u=read(),v=read(),w=read(); add(u,v,w); } dijkstra(1); if(!f[n][0]) puts("-1"); else write(dis[n][0]); return 0; }
- 1
信息
- ID
- 9309
- 时间
- 1000ms
- 内存
- 500MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者