题解:AT_agc072_e [AGC072E] Flights 2
[AGC072E] Flights 2
给定一张
n 个点m 条边的有向图和正整数F ,每条边有边权w_i ,通过这条边需要花费F\times w_i 元,并获得w_i 里程。在点i 时,你可以将x 里程兑换为r_ix 元(x 可以为任意正实数)。初始时你在机场1 ,有0 里程,求要到达n 初始时至少要带多少钱。多组数据。
\sum n^2\le 160000,m\le n(n-1),r_i < F\le 100,w_i\le 100 。
神仙题。
有个朴素 dp,设
考虑证明,如果某次兑换既没有把里程花完,到下一个兑换点时也没有把钱花完,假设这两个点分别为
接下来我们就不用记录
-
从
f_u 走到f_v ,那么一定是直接从u 走到v ,并在v 时将所有里程兑换为钱,条件为:\left\{\begin{matrix}f_u\ge d_{u,v}F \\ f_u-d_{u,v}F+d_{u,v}r_v\ge f_v\end{matrix}\right. ,于是转移为f_u\larr \max(f_v-d_{u,v}r_v,0)+d_{u,v}F 。 -
从
f_u 走到g_v ,那么中间可能存在一个兑换点x ,先从u 走到x ,然后在x 处将一些里程兑换为钱,然后走到v 时钱正好花光。可以发现中间的兑换点至多一个,要不然中间就会出现其他的里程或钱为0 的状态。我们枚举这个x ,如果x=u 就表示没有中间的兑换点,条件为:\left\{ \begin{matrix}f_u\ge d_{u,x}F \\ d_{u,x}\ge \frac{d_{x,v}F-(f_u-d_{u,x}F)}{r_x} \\ d_{u,x}-\frac{d_{x,v}F-(f_u-d_{u,x}F)}{r_x}+d_{x,v} \ge g_v \end{matrix} \right. 其中
\frac{d_{x,v}F-(f_u-d_{u,x}F)}{r_x} 表示在点x 时需要用多少里程换钱,于是转移条件为d_{u,x}+d_{x,v}\ge g_v ,转移式子为:f_u\larr \max(d_{x,v}F-\min(d_{u,x},d_{u,x}+d_{x,v}-g_v)\times r_x)+d_{u,x}F 。 -
从
g_u 走到g_v ,那么一定是在u 时用一些里程兑换为钱,走到v 时钱正好花光,条件为\left\{\begin{matrix}g_u\ge \frac{d_{u,v}F}{r_u} \\ g_u-\frac{d_{u,v}F}{r_u}+d_{u,v}\ge g_v\end{matrix}\right. ,转移为g_u\larr \max(g_v-d_{u,v},0)+\frac{d_{u,v}F}{r_u} 。 -
从
g_u 走到f_v ,那么一定是在u 时用一些里程兑换为钱,走到v 时钱正好花光,然后再将所有此时的所有里程兑换为钱。可以发现前面部分就是从g_u 走到g_v 时做的转移,于是我们只需要处理后面部分将所有里程兑换为钱,即g_v\larr \frac{f_v}{r_v} 。
可以发现一个点不会重复经过,因为
考虑优化,我们尝试找出一个在不断走的过程中保持单调的量,设
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define up(x,y) x = min(x,y)
#define ll long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int N = 405;
const double eps = 1e-10;
int d[N][N],n,m;
double f[N][2],r[N],F;
bool vis[N][2];
inline double get(int i,int o){return f[i][o]*(o?F:1);}
void solve()
{
int u = 0,tp = 0;double v = 1e18;
for(int i = 1;i <= n;i++)for(int o : {0,1})
if(!vis[i][o]&&get(i,o) < v)v = get(i,o),u = i,tp = o;
vis[u][tp] = 1;v = f[u][tp];
if(!tp)
{
if(r[u] > eps)up(f[u][1],v/r[u]);
for(int i = 1;i <= n;i++)up(f[i][0],max(v-d[i][u]*r[u],0.0)+d[i][u]*F);
}
else for(int i = 1;i <= n;i++)
{
if(r[u] > eps)up(f[i][1],max(v-d[i][u],0.0)+d[i][u]*F/r[i]);
for(int j = 1;j <= n;j++)if(d[i][j]+d[j][u] > v-eps)
up(f[i][0],max(d[j][u]*F-min(d[i][j]*1.0,d[i][j]+d[j][u]-v)*r[j],0.0)+d[i][j]*F);
}
}
char buf[1<<21],*p1,*p2;
inline int rd()
{
char c;int f = 1;
while(!isdigit(c = getchar()))if(c=='-')f = -1;
int x = c-'0';
while(isdigit(c = getchar()))x = x*10+(c^48);
return x*f;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
for(int T = rd();T--;)
{
n = rd();m = rd();F = rd();
for(int i = 1;i <= n;i++)for(int o : {0,1})
f[i][o] = i==n?0:1e9,vis[i][o] = 0;
for(int i = 1;i <= n;i++)for(int j = 1;j <= n;j++)d[i][j] = i==j?0:1e9;
for(int i = 1;i <= m;i++)
{int u = rd(),v = rd(),w = rd();d[u][v] = min(d[u][v],w);}
for(int i = 1;i <= n;i++)r[i] = rd();
for(int k = 1;k <= n;k++)for(int i = 1;i <= n;i++)for(int j = 1;j <= n;j++)
d[i][j] = min(d[i][j],d[i][k]+d[k][j]);
for(int t = n*2;t--;)solve();
printf("%.10lf\n",f[1][0]);
}
return 0;
}