(放不了题面。)
S o l u t i o n \mathfrak{Solution} Solution
一道很像最大流的费用流。
1
第一眼看过去觉得是分层图。原因:到某个节点时车的状态(第几天)是不同的。
但数据范围明显不让你这么干。
然后就不难想到二分。
二分的明显是天数。
2
如何判断当前天数能否满足题意?
使用增广路来实现。每次找源点和汇点之间的增广路径,因为每走一条路用一天,所以不妨设路径长度都为 1。那么这条增广路的长度就是走这条路所用的天数,记为 d i s n dis_n disn。它等价于费用流里的费用。
对于每条路径的限制,将它等价于费用流中的流量。那么最后能从源点走此增广路到汇点的流数量 即为 能同时花 d i s n dis_n disn 天,走此条增广路到终点的人数,记为 i n c f n incf_n incfn。
综上,对于一条增广路径而言,假设当前二分出的天数为 m i d mid mid,有:在 m i d mid mid 天中,能走这条路到终点的总人数为 ( m i d − d i s n + 1 ) × i n c f n (mid - dis_n +1) \times incf_n (mid−disn+1)×incfn。这个很好理解。
3
二分的 c h e c k ( ) check() check() 函数:
对于二分到的一个总天数 m i d mid mid,我们反复寻找增广路经,每找到一条就用 k k k(总人数)减去能走此增广路经的人总数,若 k ≤ 0 k\le 0 k≤0,证明 m i d mid mid 可行。
A C C o d e \mathfrak{AC Code} ACCode
注意每次 c h e c k ( ) check() check() 函数前都要重新建边。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define maxn 2005
#define maxm 10005
#define inf 2147483647
int n, m, k;
int cnt = 1, hd[maxn];
struct node{
int to, nxt, flw, cst;
}e[maxm];
int ui[maxm], vi[maxm], fi[maxm];
int dis[maxn], incf[maxn];
int pre[maxn];
int l, r;
int s, t;
bool vis[maxn];
inline void init()
{
l = r = s = t = 0;
cnt = 1;
}
inline void add(int u, int v, int f)
{
e[++cnt] = (node){v, hd[u], f, 1};
hd[u] = cnt;
e[++cnt] = (node){u, hd[v], 0, -1};
hd[v] = cnt;
}
inline int read()
{
int x = 1, ss = 0;
char ch = getchar();
while(ch < '0' or ch > '9'){if(ch == '-') x = -1; ch = getchar();}
while(ch >= '0' and ch <= '9') ss = ss * 10 + ch - '0', ch = getchar();
return x * ss;
}
inline bool spfa()
{
queue <int> q;
rep(i, 0, n + 10) dis[i] = inf;
memset(vis, 0, sizeof vis);
vis[1] = 1, q.push(1), dis[1] = 0;
incf[1] = inf;
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = 0;
for(int i = hd[u], v; i; i = e[i].nxt)
{
if(!e[i].flw) continue;
if(dis[v = e[i].to] > dis[u] + e[i].cst)
{
dis[v] = dis[u] + e[i].cst;
incf[v] = min(incf[u], e[i].flw);
pre[v] = i;
if(!vis[v])
vis[v] = 1, q.push(v);
}
}
}
return dis[n] != dis[n + 1];
}
inline bool check(int day)
{
int tmp = k;
while(spfa())
{
if(day - dis[n] + 1 < 1) return 0;
tmp -= (day - dis[n] + 1) * incf[n];
if(tmp <= 0) return 1;
int x = n, i;
while(x != 1)
{
i = pre[x];
e[i].flw -= incf[n], e[i ^ 1].flw += incf[n];
x = e[i ^ 1].to;
}
}
return 0;
}
signed main()
{
int u, v, f;
while(scanf("%d%d%d", &n, &m, &k) != EOF)
{
init();
rep(i, 1, m)
{
ui[i] = read() + 1, vi[i] = read() + 1,
fi[i] = read();
}
if(!k)
{
printf("0\n");
continue;
}
l = 0, r = k << 1;
int flg = -1;
while(l < r)
{
int mid = l + r >> 1;
cnt = 1, memset(hd, 0, sizeof hd);
rep(i, 1, m) add(ui[i], vi[i], fi[i]);
if(check(mid)) r = flg = mid;
else l = mid + 1;
}
if(~flg) printf("%d\n", flg);
else puts("No solution");
}
return 0;
}
—— E n d \mathfrak{End} End——