1. 程式人生 > >[COGS 2401]Time is Money

[COGS 2401]Time is Money

esp gin get 條件 scrip struct spl 最小值 mes

Description

題庫鏈接

給你 \(n\) 個節點 \(m\) 條邊的無向連通圖。每條邊有兩個權值 \(c,t\) ,要你生成一棵邊集為 \(\mathbb{E}\) 的生成樹使得 \[\sum_{e\in \mathbb{E}}e_c \times \sum_{e\in \mathbb{E}}e_t\] 最小。

\(1\leq n\leq 200,1\leq m\leq 10000\)

Solution

\(x=\sum_{e\in \mathbb{E}}e_c,y=\sum_{e\in \mathbb{E}}e_t\) 。記 \(k=xy\) ,那麽對於反比例函數 \(y=\frac{k}{x}\)

\(k\) 越小越貼近坐標軸。

我們將所有生成樹的權值以 \((x,y)\) 的形式刻畫在坐標軸上,顯然滿足條件的最小值一定在左下凸包上。

那麽首先最極端的是單純按 \(c\) 排序和按 \(t\) 排序得到的點 \(A,B\)

然後顯然要找到在連線 \(AB\) 左下最遠的點 \(C\)

由於離 \(AB\) 最遠的 \(C\) 必定導致 \(S_{\Delta ABC}\) 最大。

顯然就是求 \(\left(\vec{AB}\times\vec{AC}\right)_{min}\) \[\begin{aligned}\vec{AB}\times\vec{AC}&=(x_B-x_A)(y_C-y_A)-(y_B-y_A)(x_C-x_A)\\&=y_C(x_B-x_A)+x_C(y_A-y_B)+K\end{aligned}\]

其中 \(K\) 是與 \(C\) 無關的量,可以忽略。所以將所有邊權改為上述式子後做一遍最小生成樹就可以求出 \(C\) 了。

然後繼續分治對線段 \(AC\) 和線段 \(CB\) 求解即可。遞歸的邊界為 \(\vec{AB}\times\vec{AC}\geq 0\)

Code

//It is made by Awson on 2018.3.7
#include <bits/stdc++.h>
#define LL long long
#define dob complex<double>
#define Abs(a) ((a) < 0 ? (-(a)) : (a))
#define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) #define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b)) #define writeln(x) (write(x), putchar('\n')) #define lowbit(x) ((x)&(-(x))) using namespace std; const int N = 200, M = 10000; void read(int &x) { char ch; bool flag = 0; for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar()); for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar()); x *= 1-2*flag; } void print(int x) {if (x > 9) print(x/10); putchar(x%10+48); } void write(int x) {if (x < 0) putchar('-'); print(Abs(x)); } int n, m, fa[N+5]; struct tt { int u, v, w, c, t; bool operator < (const tt &b) const {return w < b.w; } }edge[M+5]; struct node { int x, y; node(int _x = 0, int _y = 0) {x = _x, y = _y; } int operator * (const node &b) const {return x*b.y-y*b.x; } node operator + (const node &b) {node t; t.x = x+b.x, t.y = y+b.y; return t; } node operator - (const node &b) {node t; t.x = x-b.x, t.y = y-b.y; return t; } void print() {write(x), putchar(' '), writeln(y); } }ans, A, B; int find(int x) {return fa[x] ? fa[x] = find(fa[x]) : x; } void update(node &ans, node x) {if (1ll*ans.x*ans.y > 1ll*x.x*x.y || (1ll*ans.x*ans.y == 1ll*x.x*x.y && ans.x > x.x)) ans = x; } bool judge(node A, node B, node C) {return (B-A)*(C-A) >= 0; } node kruskal() { memset(fa, 0, sizeof(fa)); int cnt = 0; node tmp; sort(edge+1, edge+1+m); for (int i = 1; i <= m; i++) { if (find(edge[i].u)^find(edge[i].v)) { ++cnt; fa[find(edge[i].u)] = find(edge[i].v); tmp = tmp+node(edge[i].c, edge[i].t); if (cnt == n-1) break; } } return tmp; } void doit(node A, node B) { int x = B.x-A.x, y = A.y-B.y; node C; for (int i = 1; i <= m; i++) edge[i].w = x*edge[i].t+y*edge[i].c; update(ans, C = kruskal()); if (judge(A, B, C)) return; doit(A, C), doit(C, B); } void work() { read(n), read(m); for (int i = 1; i <= m; i++) read(edge[i].u), read(edge[i].v), read(edge[i].c), read(edge[i].t), ++edge[i].u, ++edge[i].v; for (int i = 1; i <= m; i++) edge[i].w = edge[i].c; A = ans = kruskal(); for (int i = 1; i <= m; i++) edge[i].w = edge[i].t; update(ans, B = kruskal()); doit(A, B); ans.print(); } int main() { work(); return 0; }

[COGS 2401]Time is Money