1 条题解

  • 0
    @ 2025-8-24 22:11:49

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar xgzc
    苔花如米小,也学牡丹开。

    搬运于2025-08-24 22:11:49,当前版本为作者最后更新于2019-08-30 20:24:53,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    ai\sum a_ibi\sum b_i分别看成xxyy,并将它投射到平面直角坐标系上,那么我们就是想找到x×yx \times y最小的点。

    先找出xx最小的点A\mathrm{A}以及yy最小的点B\mathrm B,接下来我们就需要求出一个点C\mathrm C,使得C\mathrm CAB\mathrm {AB}左侧且C\mathrm C距离AB\mathrm {AB}最远。

    也就是让SABCS_{\triangle ABC}最大,即$\overrightarrow{\mathrm{AB}} \times \overrightarrow{\mathrm{AC}}$最小(因为$\overrightarrow{\mathrm{AB}} \times \overrightarrow{\mathrm{AC}} < 0$)

    tree.png

    $$\begin{aligned}\because \overrightarrow{\mathrm{AB}} \times \overrightarrow{\mathrm{AC}} &= (x_{\mathrm{B}} - x_{\mathrm{A}})(y_{\mathrm{C}} - y_{\mathrm{A}}) - (y_{\mathrm{B}} - y_{\mathrm{A}})(x_\mathrm{C} - x_\mathrm{A}) \\&= (x_\mathrm B - x_\mathrm A) \times y_\mathrm C + (y_\mathrm A - y_\mathrm B) \times x_\mathrm C + y_\mathrm B x_\mathrm A - x_\mathrm B y_\mathrm A\end{aligned} $$

    所以考虑最小化$(x_\mathrm B - x_\mathrm A) \times y_\mathrm C + (y_\mathrm A - y_\mathrm B) \times x_\mathrm C$。

    于是可以直接将边权改为$w_i = (y_\mathrm A - y_\mathrm B) \times a_i + (x_\mathrm B - x_\mathrm A)\times b_i$,跑最小生成树算法即可得到C\mathrm C点。

    接下来递归处理AC,CB\mathrm {AC, CB}即可。

    这个过程中我们可以知道,决策点构成了一个含有n!n!个点的凸包。因为在nn个点的凸包上的点数期望是lnn\sqrt {\ln n}的,所以复杂度为O(nlognlnn!)\mathrm{O}(n\log n\sqrt {\ln n!})

    #include <cstdio>
    #include <algorithm>
    #include <vector>
    
    inline int read()
    {
    	int data = 0, w = 1; char ch = getchar();
    	while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    	if (ch == '-') w = -1, ch = getchar();
    	while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
    	return data * w;
    }
    
    const int N(205), M(10010);
    struct vector { int x, y; }; vector ans = (vector) {(int) 1e9, (int) 1e9};
    inline vector operator - (const vector &lhs, const vector &rhs)
    	{ return (vector) {lhs.x - rhs.x, lhs.y - rhs.y}; }
    inline int operator * (const vector &lhs, const vector &rhs)
    	{ return lhs.x * rhs.y - lhs.y * rhs.x; }
    void chkmin(vector &x, vector y)
    {
    	long long _x = 1ll * x.x * x.y, _y = 1ll * y.x * y.y;
    	if (_x > _y || (_x == _y && x.x > y.x)) x = y;
    }
    
    int n, m, fa[N], rnk[N];
    int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
    int merge(int x, int y)
    {
    	x = find(x), y = find(y);
    	return (rnk[x] == rnk[y] ? fa[x] = y, ++rnk[y] :
    			(rnk[x] < rnk[y] ? fa[x] = y : fa[y] = x));
    }
    
    struct edge { int x, y, a, b, w; } e[M];
    inline int operator < (const edge &lhs, const edge &rhs) { return lhs.w < rhs.w; }
    vector Kruskal()
    {
    	vector res = (vector) {0, 0}; int cnt = 0;
    	for (int i = 1; i <= n; i++) fa[i] = i, rnk[i] = 1;
    	std::sort(e + 1, e + m + 1);
    	for (int i = 1; i <= m && cnt < n - 1; i++)
    	{
    		int x = find(e[i].x), y = find(e[i].y);
    		if (x != y) merge(x, y), res.x += e[i].a, res.y += e[i].b, ++cnt;
    	}
    	return res;
    }
    
    void solve(const vector &A, const vector &B)
    {
    	for (int i = 1; i <= m; i++)
    		e[i].w = e[i].a * (A.y - B.y) + e[i].b * (B.x - A.x);
    	vector C = Kruskal(); chkmin(ans, C);
    	if ((B - A) * (C - A) >= 0) return;
    	solve(A, C), solve(C, B);
    }
    
    int main()
    {
    	n = read(), m = read();
    	for (int i = 1; i <= m; i++)
    		e[i] = (edge) {read() + 1, read() + 1, read(), read(), 0};
    	for (int i = 1; i <= m; i++) e[i].w = e[i].a;
    	vector A = Kruskal(); chkmin(ans, A);
    	for (int i = 1; i <= m; i++) e[i].w = e[i].b;
    	vector B = Kruskal(); chkmin(ans, B);
    	solve(A, B); printf("%d %d\n", ans.x, ans.y);
    	return 0;
    }
    
    • 1

    信息

    ID
    4532
    时间
    2000ms
    内存
    64MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者