网上题解讲的好清楚好清楚了。。
令
x=∑c,y=∑t
,要最优化
xy
,如果将其表示成
(x,y)
,那么最优的
(x,y)
只会在下凸壳上,那么分治?
分治将凸壳不断地划分,至于划分点有一个很显然的在凸壳上的就是离两端点最远的点C(在坐标轴侧)。
求C显然是最大化
S△ABC=12AB×d=12AC−→−×AB−→
maxAC−→−×AB−→ =(cx−ax,cy−ay)×(bx−ax,by−ay)=(cx−ax)(by−ay)−(cy−ay)(bx−ax)=cx(by−ay)−cy(bx−ax)+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%的数据如上所述