Description
Solution
一道差分约束好题。
题目要求我们输出最大的 \(T\) 使得至少有一个人女装。
那么容易想到二分答案。
所以这道题就很明显了,二分答案套差分约束,判断是否符合条件。
再来看如何建图。
-
对于 \(C_A > C_B * k\),我们从 \(B\) 向 \(A\) 连一条权值为 \(k\) 的边。
-
对于已知分数的选手 \(C_i = k\),我们设一个 0 节点,且 \(S_0 = 1\),然后我们从 0 向 \(i\) 连权值为 \(k\) 的边,从 \(i\) 向 0 连权值为 \(\frac {1}{k}\) 的边。
然后跑一遍最长路,判断是否有环。
如果有就符合条件。
注意:这里的最长路是乘法运算
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define eps 1e-8
#define ll long long
using namespace std;
const ll N = 1010;
ll n, s, t;
double l, r;
struct Upd{
ll op, a, b, k;
}tmp[N];
ll c[N];
struct node{
ll v, nxt;
double w;
}edge[N << 1];
ll head[N], tot;
double dis[N];
ll vis[N], cnt[N];
queue <ll> q;
inline void add(ll x, ll y, double z){
edge[++tot].v = y;
edge[tot].w = z;
edge[tot].nxt = head[x];
head[x] = tot;
}
inline bool spfa(){
while(!q.empty()){
ll x = q.front();
for(ll i = head[x]; i; i = edge[i].nxt){
ll y = edge[i].v;
if(dis[y] >= dis[x] * edge[i].w) continue;
dis[y] = dis[x] * edge[i].w;
cnt[y] = cnt[x] + 1;
if(cnt[y] == n + 2) return 1;
if(!vis[y]) q.push(y), vis[y] = 1;
}
q.pop(), vis[x] = 0;
}
return 0;
}
inline bool check(double T){
memset(head, 0, sizeof(head));
memset(cnt, 0, sizeof(cnt));
while(!q.empty()) q.pop();
tot = 0;
dis[0] = vis[0] = 1;
q.push(0);
for(ll i = 1; i <= n; i++){
dis[i] = vis[i] = 1;
q.push(i);
if(c[i]) add(i, 0, 1.0 / c[i]), add(0, i, c[i]);
}
for(ll i = 1; i <= s; i++){
if(tmp[i].op == 1) add(tmp[i].b, tmp[i].a, tmp[i].k - T);
else add(tmp[i].b, tmp[i].a, 1.0 / (tmp[i].k + T));
}
return spfa();
}
signed main(){
scanf("%lld%lld%lld", &n, &s, &t);
l = 0, r = 1e18;
for(ll i = 1; i <= s; i++){
ll op, a, b, k;
scanf("%lld%lld%lld%lld", &op, &a, &b, &k);
tmp[i] = (Upd){op, a, b, k};
if(op == 1) r = min(r, 1.0 * k);
}
for(ll i = 1; i <= t; i++){
ll p, x;
scanf("%lld%lld", &p, &x);
c[p] = x;
}
double ans = -1;
while(r - l > eps){
double mid = (l + r) / 2;
if(check(mid)) ans = mid, l = mid;
else r = mid;
}
if(ans == -1) puts("-1");
else printf("%.10lf\n", ans);
return 0;
}