2022牛客多校第二场D.Link with Game Glitch
题意:有 \(n\) 种物品,给定 \(m\) 种交换方式,每种交换方式用 \(a,b,c,d\) 表示,表示可以用 \(b\) 按照 \(a : c\) 的比例换 \(d\) ,现在给定一个系数 \(w\),使得该比例变成 \(a : (w*c)\) ,求一个最大的 \(w\) 使得不存在一种方案可以无限换物品
知识点:建图,求负环
容易得到一种建图方式,对 \(b\) 和 \(d\) 建立起一条边权为 \(\frac{c}{a}\) 边,然后对于 \(w\) 二分查找验证即可。但是如果这样的话,最坏就是 \(1000^n\) 的级别的乘法,会爆掉,所以可以用对数加来代替边权乘
#include <bits/stdc++.h>
#define endl '\n'
#define double long double
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, double> PID;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 1e5 + 10;
const int MOD = 1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}
inline void solve() {
int n, m; cin >> n >> m;
vector<PID> g[n + 2];
for (int i = 1; i <= m; i ++ ) {
int a, b, c, d;
cin >> a >> b >> c >> d;
g[b].emplace_back(d, log((double)c / (double)a));
}
for (int i = 1; i <= n; i ++ ) g[n + 1].emplace_back(i, 0);
auto check = [&](double w) {
w = log(w);
vector<double> dis(n + 2, 1e18);
vector<int> cnt(n + 2);
vector<bool> vis(n + 2);
queue<int> q;
q.push(n + 1);
dis[n + 1] = 0;
vis[n + 1] = true;
while (!q.empty()) {
int t = q.front(); q.pop();
vis[t] = false;
for (auto ite : g[t]) {
int v = ite.first;
double tmp = ite.second;
if (dis[v] > dis[t] + tmp + w) {
dis[v] = dis[t] + tmp + w;
cnt[v] = cnt[t] + 1;
if (cnt[v] > n + 1) return true;
if (!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
return false;
};
double l = 0, r = 1;
for (int i = 1; i <= 100; i ++ ) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
cout << l << endl;
}
signed main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
auto now = clock();
#endif
ios::sync_with_stdio(false), cin.tie(nullptr);
cout << fixed << setprecision(10);
// int T; cin >> T;
// while (T -- )
solve();
#ifdef DEBUG
cout << "============================" << endl;
cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
return 0;
}