zoukankan      html  css  js  c++  java
  • 【BZOJ】2395: [Balkan 2011]Timeismoney

    题解

    最小乘积生成树!

    我们把,x的总和和y的总和作为x坐标和y左边,画在坐标系上

    我们选择两个初始点,一个是最靠近y轴的A,也就是x总和最小,一个是最靠近x轴的B,也就是y总和最小
    连接两条直线,在这条直线上面的点都不用考虑了

    我们选一个离直线最远的点C,且在直线下方,我们用叉积考虑这个东西,也就是……面积最大!我们如果用最小生成树的话,只要让面积是负的就好了
    推一下式子,发现是((A.y - B.y) * C.x + (B.x - A.x) * C.y)我们发现就是把边设置成
    ((A.y - B.y) * E[i].c + (B.x - A.x) * E[i].t)做一遍最小生成树

    找到C点后递归处理A,C和C,B即可

    边界是两点连线下方没有点也就是叉积大于等于0

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <ctime>
    #include <vector>
    #include <set>
    //#define ivorysi
    #define eps 1e-8
    #define mo 974711
    #define pb push_back
    #define mp make_pair
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define MAXN 10005
    #define space putchar(' ')
    #define enter putchar('
    ')
    using namespace std;
    typedef long long int64;
    typedef unsigned int u32;
    typedef unsigned long long u64;
    typedef double db;
    const int64 MOD = 1000000007;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) putchar('-');
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    int N,M;
    struct Point {
        int64 x,y;
        int64 v;
        Point(){};
        Point(int64 _x,int64 _y) {
    	x = _x;y = _y;v = x * y;
        }
        friend bool operator < (const Point &a,const Point &b) {
    	return a.v < b.v || (a.v == b.v && a.x < b.x);
        }
    }ans;
    struct Edge {
        int u,v;
        int64 c,t,w;
        Edge(){}
        Edge(int _u,int _v,int64 _c,int64 _t) {
    	u = _u;v = _v;c = _c;t = _t;
        }
        friend bool operator < (const Edge &a,const Edge &b) {
    	return a.w < b.w || (a.w == b.w && a.c < b.c);
        }
    }E[MAXN];
    int fa[205];
    int getfa(int u) {
        return fa[u] == u ? u : fa[u] = getfa(fa[u]);
    }
    Point kruskal() {
        sort(E + 1,E + M + 1);
        Point res = Point(0,0);
        for(int i = 1 ; i <= N ; ++i) fa[i] = i;
        for(int i = 1 ; i <= M ; ++i) {
    	if(getfa(E[i].u) != getfa(E[i].v)) {
    	    fa[getfa(E[i].u)] = getfa(E[i].v);
    	    res.x += E[i].c;res.y += E[i].t;
    	}
        }
        res.v = res.x * res.y;
        if(res < ans) ans = res;
        return res;
    }
    void Work(Point A,Point B) {
        for(int i = 1 ; i <= M ; ++i) {
    	E[i].w = (A.y - B.y) * E[i].c + (B.x - A.x) * E[i].t;
        }
        Point r = kruskal();
        if((A.x - r.x) * (B.y - r.y) - (A.y - r.y) * (B.x - r.x) >= 0) return;
        Work(A,r);
        Work(r,B);
    }
    void Solve() {
        read(N);read(M);
        int u,v;
        int64 c,t;
        for(int i = 1 ; i <= M ; ++i) {
    	read(u);read(v);read(c);read(t);
    	++u;++v;
    	E[i] = Edge(u,v,c,t);
        }
        ans.v = 1e18;
        for(int i = 1 ; i <= M ; ++i) {
    	E[i].w = E[i].c;
        }
        Point A = kruskal();
        for(int i = 1 ; i <= M ; ++i) {
    	E[i].w = E[i].t;
        }
        Point B = kruskal();
        Work(A,B);
        printf("%lld %lld
    ",ans.x,ans.y);
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Solve();
        return 0;
    }
    
  • 相关阅读:
    学习 TList 类的实现[1]
    Dll 使用 PChar 参数的小例子
    学习使用资源文件[11]
    【转载】OGRE 内存管理
    【转载】Ogre的内存分配策略
    【转载】.NET面试题系列[0]
    【转载】深入探讨透视投影坐标变换
    【转载】四元数-Quaterion
    【转载】齐次坐标
    【转载】深入研究Windows内部原理绝对经典的资料
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9071158.html
Copyright © 2011-2022 走看看