题解 P3778 【[APIO2017]商旅】
首先这题并不卡精度。二分的时候只二分整数就没有任何问题了。。。
裸分数规划+Folyd, 预处理出任意两点的距离
#include <iostream>
#define ll long long
using namespace std;
int const N=101,K=1001,L=8;
ll const INF=1e9;
int n,m,t;
ll s[N][K],b[N][K],earn[N][N],g[N][N],f[N][N];
bool find(ll r){
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i==j) f[i][j]=-INF; else f[i][j]=earn[i][j]-r*g[i][j];
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
f[i][j]=max(f[i][j],f[i][k]+f[k][j]);
ll now=-INF;
for (int i=1;i<=n;i++) now=max(now,f[i][i]);
return now>=0;
}
ll get(){
ll l=1,r=1e9;
while (l<r-1){
ll mid=(l+r)/2;
if (find(mid)) l=mid; else r=mid-1;
}
if (find(r)) return r;
if (find(l)) return l;
return l-1;
}
int main(){
cin>>n>>m>>t;
for (int i=1;i<=n;i++)
for (int j=1;j<=t;j++){
cin>>b[i][j]>>s[i][j];
if (b[i][j]==-1) b[i][j]=INF;
if (s[i][j]==-1) s[i][j]=0;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int k=1;k<=t;k++)
earn[i][j]=max(earn[i][j],s[j][k]-b[i][k]);
for (int i=1;i<=m;i++){
int x,y;
ll z;
cin>>x>>y>>z;
g[x][y]=z;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (!g[i][j]) g[i][j]=INF;
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (!g[i][j] || g[i][k]+g[k][j]<g[i][j]) g[i][j]=g[i][k]+g[k][j];
cout<<get()<<endl;
return 0;
}