题解 舰队游戏
令 \(f_{i, j}\) 为在位置 \(i\) 剩 \(j\) 点血
那么有
\[f_{i, j}=\min(f_{1, h}+h-j, f_{v, j-d_v})
\]
带着这个 min 完全没法消元什么的
那怎么办呢?
考虑变元只有 \(f_{1, h}\),设它为 \(x\)
那么所有转移路径的值都可以用 \(x\) 表示成 \(kx+b\) 的形式
是在所有转移路径中最小值,所以可以建立凸包
那么这个凸包与 \(y=x\) 的交点坐标即为答案
那么可以二分这个交点
check 按逆拓扑序转移即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 110
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m, h;
bool vis[N];
int deg[N], d[N], dsu[N];
pair<int, int> e[N*N];
vector<int> to[N];
inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
// namespace task2{
// void solve() {
// int sum=0;
// for (int i=1; i<=n; ++i) sum+=d[i];
// if (sum>=h) puts("-1");
// else printf("%.10lf\n", double(n-1));
// }
// }
namespace task1{
const double eps=1e-10;
struct matrix{
int n, m;
double a[110][110];
matrix(){n=m=0; memset(a, 0, sizeof(a));}
matrix(int x, int y) {n=x; m=y; memset(a, 0, sizeof(a));}
void resize(int x, int y) {n=x; m=y; memset(a, 0, sizeof(a));}
inline double* operator [] (int t) {return a[t];}
void put() {for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) cout<<setw(3)<<a[i][j]<<' '; cout<<endl;}cout<<endl;}
double gauss() {
for (int i=1; i<=n; ++i) {
int r=i;
for (int j=i+1; j<=n; ++j) if (fabs(a[j][i])>fabs(a[r][i])) r=j;
swap(a[r], a[i]);
for (int j=1; j<=n; ++j) if (i!=j) {
double t=a[j][i]/a[i][i];
for (int k=i; k<=m; ++k) a[j][k]-=t*a[i][k];
}
}
return a[1][n+1]/a[1][1];
}
}mat;
void solve() {
if (h<=d[n]) {puts("-1"); return ;}
for (int i=1; i<=n; ++i) dsu[i]=i;
for (int i=1; i<=m; ++i) dsu[find(e[i].fir)]=find(e[i].sec);
if (find(1)!=find(n)) {puts("-1"); return ;}
mat.resize(n, n+1);
for (int i=1; i<n; ++i) {
mat[i][i]=-1;
if (deg[i]) {
if (i!=n) mat[i][n+1]=-1;
double t=1.0/deg[i];
for (auto v:to[i]) mat[i][v]=t;
}
else mat[i][1]=1;
}
mat[n][n]=-1;
// mat.put();
printf("%.10lf\n", mat.gauss());
// mat.put();
}
}
namespace task{
int hp[N];
bool able[N];
double f[N][N];
void solve() {
hp[1]=h; able[n]=1;
for (int i=2; i<=n; ++i) hp[i]=-INF;
for (int i=1; i<=n; ++i) for (auto v:to[i]) hp[v]=max(hp[v], hp[i]-d[v]);
for (int i=n-1; i; --i) {
for (auto v:to[i]) if (able[v]) able[i]=1;
for (auto v:to[i]) if (hp[i]<=d[v]) able[i]=0;
}
// cout<<"able: "; for (int i=1; i<=n; ++i) cout<<able[i]<<' '; cout<<endl;
if (!able[1]) {puts("-1"); return ;}
double l=1, r=1000001, mid;
for (int i=1; i<=60; ++i) {
mid=(l+r)/2;
// cout<<"mid: "<<mid<<endl;
memset(f, 0x42, sizeof(f));
for (int j=1; j<=h; ++j) f[n][j]=0;
for (int j=n-1; j; --j) {
for (int k=1; k<=h; ++k) {
bool any=0; double tem=0;
for (auto v:to[j]) {
if (d[v]>=k) any=1;
tem+=f[v][k-d[v]];
}
if (deg[j]) tem/=deg[j];
if (j!=1) f[j][k]=min(f[j][k], h-k+mid);
if (deg[j]&&!any) f[j][k]=min(f[j][k], tem+1);
}
}
// cout<<"f: "<<f[1][h]<<endl;
if (f[1][h]>mid) l=mid;
else r=mid;
// cout<<"---f---"<<endl;
// for (int i=1; i<=n; ++i) {cout<<i<<": "; for (int j=1; j<=h; ++j) cout<<setw(15)<<f[i][j]<<' '; cout<<endl;}
}
if (l>1000000) puts("-1");
else printf("%.10lf\n", l);
}
}
signed main()
{
freopen("kancolle.in", "r", stdin);
freopen("kancolle.out", "w", stdout);
n=read(); m=read(); h=read();
bool all_diff=1;
for (int i=1,u,v; i<=m; ++i) {
u=read(); v=read();
e[i]={u, v};
++deg[u]; to[u].pb(v);
if (vis[v]) all_diff=0;
else vis[v]=1;
}
for (int i=1; i<=n; ++i) d[i]=read();
// if (m==n-1 && all_diff) task2::solve();
task::solve();
return 0;
}