BZOJ 2395 Balkan 2011 Time is Money 最小乘积生成树

网上题解讲的好清楚好清楚了。。

x=c,y=t ,要最优化 xy ,如果将其表示成 (x,y) ,那么最优的 (x,y) 只会在下凸壳上,那么分治?
分治将凸壳不断地划分,至于划分点有一个很显然的在凸壳上的就是离两端点最远的点C(在坐标轴侧)。
求C显然是最大化

SABC=12AB×d=12AC×AB

maxAC×AB =(cxax,cyay)×(bxax,byay)=(cxax)(byay)(cyay)(bxax)=cx(byay)cy(bxax)+C=c×AB+C

那么按照上式求生成树的结果就是C。

#include <cstdio>
#include <algorithm>
using namespace std;
#define rep(i,j,k) for(int i=j;i<k;++i)
typedef long long ll;
const int N = 200, M = 10000, inf = 1<<20;
struct Edge { int u, v, c, t, w; } e[M];
bool operator< (Edge a, Edge b) { return a.w < b.w; }
struct Point { ll x, y; };
Point operator- (Point a, Point b) { return (Point) {a.x - b.x, a.y - b.y }; }
int cross(Point a, Point b) { return a.x * b.y - a.y * b.x; }
int n, m, rnk[N], fa[N], h[N];
Point ans = (Point) { inf, inf };
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge(int x, int y) {
    if (rnk[x] < rnk[y]) swap(x, y);
    fa[x] = y;
    if (rnk[x] == rnk[y]) ++rnk[x];
}
Point kruskal() {
    int tot = 0; Point now = (Point) {0, 0};
    sort(e, e + m);
    memset(rnk, 0, sizeof rnk);
    rep(i,0,n) fa[i] = i;
    rep(i,0,m) {
        int x = find(e[i].u), y = find(e[i].v);
        if (x != y) {
            merge(x, y); ++tot;
            now.x += e[i].c;
            now.y += e[i].t;
            if (tot >= n - 1) break;
        }
    }
    if (ans.x * ans.y > now.x * now.y) ans = now;
    return now;
}
void divide(Point a, Point b) {
    rep(i,0,m) e[i].w = Point(e[i].c, e[i].t) * (a - b);
    Point c = kruskal();
    if (cross(b - a, c - a) >= 0) return;
    divide(a, c); divide(c, b);
}
int main() {
    scanf("%d%d", &n, &m);
    rep(i,0,m) scanf("%d%d%d%d", &e[i].u, &e[i].v, &e[i].c, &e[i].t);
    rep(i,0,m) e[i].w = e[i].c;
    Point minc = kruskal();
    rep(i,0,m) e[i].w = e[i].t;
    Point mint = kruskal();
    divide(minc, mint);
    printf("%lld %lld", ans.x, ans.y);
    return 0;
}

2395: [Balkan 2011]Timeismoney

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 429 Solved: 238
[Submit][Status][Discuss]

Description

 有n个城市(编号从0..n-1),m条公路(双向的),从中选择n-1条边,使得任意的两个城市能够连通,一条边需要的c的费用和t的时间,定义一个方案的权值v=n-1条边的费用和*n-1条边的时间和,你的任务是求一个方案使得v最小

Input

第一行两个整数n,m,接下来每行四个整数a,b,c,t,表示有一条公路从城市a到城市b需要t时间和费用c

Output

【output】timeismoney.out
仅一行两个整数sumc,sumt,(sumc表示使得v最小时的费用和,sumc表示最小的时间和) 如果存在多个解使得sumc*sumt相等,输出sumc最小的

Sample Input

5 7

0 1 161 79

0 2 161 15

0 3 13 153

1 4 142 183

2 4 236 80

3 4 40 241

2 1 65 92

Sample Output

279 501

HINT

【数据规模】

1<=N<=200

1<=m<=10000

0<=a,b<=n-1

0<=t,c<=255

有5%的数据m=n-1

有40%的数据有t=c

对于100%的数据如上所述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值