【题解】P9751 [CSP-J 2023] 旅游巴士
传送门
题意简述
给一张有向无环图,求点
- 经过这个点的时间不能小于其开放时间(即边权)
- 经过起点,终点的时间必须为
k 的倍数
思路
说明:下文中所有的
首先尝试正向思考。经过一条边需要考虑其是否到达开放时间,并且还要保证起点和终点时间必须为
如果我们考虑建反图,题目就变成了求
还有一点,建反图之后我们可以发现,到达时间
接下来是建图。为了保证到达
建好图后,二分倍数,
(意即:用
注意事项
该做法时间复杂度较高,数组的大小和二分的范围均决定了分数大小。
经过反复测试后,发现数组大小在 1900000 比较合适,二分范围在 0~1800000 比较合适,能够通过此题。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1900000;
const int inf = 2e9;
int n,m,k,t,ans,cnt=1;
int dis[N],head[N],vis[N];
struct edge{
int to,next,w;
}e[N];
void add(int u,int v,int w) {
e[++cnt].next=head[u];
head[u]=cnt;
e[cnt].to=v; e[cnt].w=w;
}
inline int read() {
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {s=s*10+ch-'0';ch=getchar();}
return s*w;
}
void print(int x)
{
if(x<0)
putchar('-'),x=-x;
if(x>9)
print(x/10);
putchar(x%10+'0');
return;
}
void init() {
for(int i=0;i<n*k;++i) vis[i]=0,dis[i]=inf;
}
bool bfs(int x) {
init();
queue<int> q;
q.push(n);
vis[n]=1; dis[n]=x*k;
while(q.size()) {
int u = q.front(); q.pop();
for(int i=head[u];i;i=e[i].next) {
int v = e[i].to,w = e[i].w;
if(dis[u]>w&&dis[v]==inf) {
dis[v]=dis[u]-1;
if(v==1) return 1;
if(vis[v]==0) {
vis[v]=1; q.push(v);
}
}
}
}
if(dis[1]==inf) return 0;
return 1;
}
void solve() {
n=read(); m=read(); k=read();
for(int i=1,u,v,w;i<=m;++i) {
u=read(); v=read(); w=read();
for(int j=1;j<k;++j) {
add(v+(j-1)*n,u+j*n,w);
}
add(v+n*(k-1),u,w);
}
int l=0,r=1800000;
while(l<r) {
int mid = l+r >> 1;
if(bfs(mid)) r=mid;
else l=mid+1;
}
int tmp = bfs(l);
if(dis[1]==inf) {
print(-1); return ;
}
print(l*k);
}
int main(){
ios::sync_with_stdio(0);
solve();
return 0;
}