Luogu 4951 [USACO 2001 OPEN]地震

水个博客玩。

$01$分数规划。

题目要求$\frac{F - \sum_{i = 1}^{n}C_i}{T_i}$最大,设$\frac{F - \sum_{i}C_i}{T_i} \geq e$,移项一下可以得到$F - \sum_{i }(e * T_i - C_i) \geq 0$。

那么在外层二分一个$e$,然后把所有边的权值设成$e * T_i - C_i$做最小生成树,假设这样子生成树的权值为$res$,如果$F - res > 0$ 那么说明答案还可以更大,否则更小。

时间复杂度$O(mlogmlogn(MaxInt))$。

Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef double db;

const int N = 405;
const int M = 10005;
const db eps = 1e-6;
const db inf = 1e10;

int n, m, ufs[N];
ll cur;

struct Pathway {
    int u, v;
    ll c, t;
    db val;
    
    friend bool operator < (const Pathway &x, const Pathway &y) {
        return x.val < y.val;
    }
    
} pat[M];

template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

int find(int x) {
    return x == ufs[x] ? x : ufs[x] = find(ufs[x]);
}

inline bool chk(db mid) {
    for(int i = 1; i <= m; i++)
        pat[i].val = mid * pat[i].t + 1.0 * pat[i].c;
        
    sort(pat + 1, pat + 1 + m);
    for(int i = 1; i <= n; i++) ufs[i] = i;
    int cnt = 0; db res = 0;
    for(int i = 1; i <= m; i++) {
        int u = find(pat[i].u), v = find(pat[i].v);
        if(u == v) continue;
        ufs[u] = v;
        ++cnt;
        res += pat[i].val;
        if(cnt >= n - 1) break;
    }
    
    return (1.0 * cur - res) > 0;
}

int main() {
    read(n), read(m), read(cur);
    for(int i = 1; i <= m; i++) 
        read(pat[i].u), read(pat[i].v), read(pat[i].c), read(pat[i].t);
    
    db ln = 0.0, rn = inf, mid, res = 0.0;
    for(; ln + eps <= rn; ) {
        mid = (ln + rn) * 0.5;
        if(chk(mid)) ln = mid, res = mid;
        else rn = mid;
    }
    
    printf("%.4f\n", res);
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/CzxingcHen/p/9894543.html