最小乘积模型
最小乘积模型
冷门算法,但没想到打 SCCPC 的时候遇到了,故写个总结。
模板题 :[P5540 BalkanOI 2011] timeismoney - 洛谷。
给出一个 \(n\) 个点 \(m\) 条边的无向图,第 \(i\) 条边有两个权值 \(a_i\) 和 \(b_i\) 。
求该图的一棵生成树 \(T\) ,使得
\[\left(\sum_{e\in T}a_e\right)\times\left(\sum_{e\in T}b_e\right)
\]
最小。
对于一个生成树,记 \(x = \sum a_i\), \(y = \sum b_i\)。可以把一个生成树用一个平面上的点 (x,y) 表示,求最小的 x * y。
不容易发现,只有在突壳上的点才可能做为答案。可以画图分讨一下得到。
于是现在就是要快速找出这个突壳上的点然后更新答案。
考虑一种不是很均匀的分治。
找到最左端的点 A (即按照权值 a 跑一个生成树),和最右端的点 B (按照权值 b 跑生成树)。
找到使得 \(S\triangle ABC\) 面积最大且在直线 AB 下放的点 C,那么 C 肯定在突壳上。
${2}S\triangle ABC = \vec {AB} \times \vec {AC} = (X_B - X_A, Y_B - Y_A) \times (X_C - X_A, Y_C - Y_A) = - X_C(Y_B - Y_A) + Y_C(X_B - X_A) + $ 常数
注意,这样叉乘出来的面积根据叉乘的法则算出来的是负数,要求面积最大就是求 \(- X_C(Y_B - Y_A) + Y_C(X_B - X_A)\) 最小。
设 \(k1 = Y_B - Y_A\),$k2 = $ \(X_B - X_A\). 相当于对 a, b 两种边权给了个系数做最小生成树。就没了。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=202,M=1e4+4,inf=1e9;
inline ll read() {
ll x = 0; bool f(0); char ch = getchar();
while(!isdigit(ch)) f = (ch == '-'), ch = getchar();
while(isdigit(ch)) x = (x<<3) + (x<<1) + (ch^48), ch = getchar();
return f ? -x : x;
}
int n, m;
ll k1, k2;
struct Edge{
int u, v;
ll a, b, w;
// inline Edge operator +=(const Edge & k) {return {u, v, a += k.a, b += k.b};} // ?
inline bool operator < (const Edge & k) const {return w ^ k.w ? w < k.w : a < k.a;}
};
struct node{
ll x, y;
inline node operator +=(const Edge & k) {return (node)(x += k.a, y += k.b);} // ?
node (int va = 0, int vb = 0) : x(va), y(vb) {}
}ans = {inf, inf};
Edge e[M];
int fa[N];
inline int find(int x) {return x ^ fa[x] ? fa[x] = find(fa[x]) : x;}
node kru() {
node res(0, 0);
sort(e + 1, e + m + 1);
for(int i = 1; i <= n; ++i) fa[i] = i;
for(int i = 1, cnt = 0; i <= m; ++i) {
if(find(e[i].u) == find(e[i].v)) continue;
fa[find(e[i].u)] = find(e[i].v);
res += e[i], ++ cnt;
if(cnt == n - 1) break;
}
return res;
}
inline ll calc(node a, node b, node c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
inline void upd(node & ans, node C) {if(ans.x * ans.y > C.x * C.y) ans = C;}
void sol(node A, node B) {
k1 = A.y - B.y, k2 = B.x - A.x;
for(int i = 1; i <= m; ++i) e[i].w = k1 * e[i].a + k2 * e[i].b;
node C = kru();
if(calc(A, B, C) >= 0) return ;
upd(ans, C);
sol(A, C), sol(C, B);
}
int main() {
n = read(), m = read();
for(int i = 1; i <= m; ++i) {
int u = read() + 1, v = read() + 1;
ll a = read(), b = read();
e[i] = {u, v, a, b, a};
}
node A = kru(); ans = A;
for(int i = 1; i <= m; ++i) e[i].w = e[i].b;
node B = kru(); upd(ans, B);
sol(A, B);
printf("%lld %lld\n", ans.x, ans.y);
return 0;
}

浙公网安备 33010602011771号