zoukankan      html  css  js  c++  java
  • 【BZOJ2395】[Balkan 2011] Timeismoney(最小乘积生成树)

    点此看题面

    • 有一张(n)个点(m)条边的无向图,每条边有两种权值(u_i,v_i)
    • 求这张图的一个生成树,最小化(sum u imessum v)
    • (nle200,mle10^4)

    构造坐标系

    听说这是一种套路,然而我从未接触过。。。

    考虑我们把(sum u)(sum v)看作二维坐标((x,y)),那么就可以构造出一张平面直角坐标系。

    那么现在我们想找的就是平面直角坐标系上(x imes y)最小的点。

    首先,我们分别找到离(y)轴和(x)轴最近的两点(A,B),这可以通过分别对(u,v)两种权值各做一次最小生成树求出。

    显然(A,B)并不一定是最优点。考虑如果存在一个(C)(A,B)更优,一个必要条件就是(C)位于直线(AB)的左下方(充要条件应该是在反比例函数左下方,但那样就麻烦多了)。

    因此,我们去找到离(AB)最远的(C),这就充要于找到使( riangle ABC)面积最大的(C)

    利用叉积公式:

    [2S_{ riangle ABC}=vec{AC} imes vec {AB} ]

    把它化开得到:

    [egin{align} 2S_{ riangle ABC}&=(x_C-x_A) imes (y_B-y_A)-(y_C-y_A) imes (x_B-x_A)\ &=x_C imes (y_B-y_A)-y_C imes (x_B-x_A)-(x_A imes (y_B-y_A)-y_A imes (x_B-x_A)) end{align} ]

    后面那一坨都是定值,因此我们要最大化(S_{ riangle ABC})就是要最大化(x_C imes (y_B-y_A)-y_C imes (x_B-x_A))

    方便起见,我们可以改成最小化(x_C imes(y_A-y_B)+y_C imes(x_B-x_A))

    然后考虑(x_C=sum u,y_C=sum v),故上式等同于(sum(u imes(y_A-y_B)+v imes(x_B-xA)))

    那么只要对(u imes(y_A-y_B)+v imes(x_B-xA))做一次最小生成树就好了。

    注意,此时的(C)仍不一定是最优点,还要递归处理(AC,CB)

    至于复杂度,我不会证明,但据说是有保证的。

    代码:(O()能过())

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200
    #define M 10000
    #define LL long long
    using namespace std;
    int n,m,ee,lnk[N+5];struct line {int x,y,u,v;}s[M+5];
    struct edge
    {
    	int p,x,y,v;I edge(CI i=0,CI a=0,CI b=0,CI c=0):p(i),x(a),y(b),v(c){}
    	I bool operator < (Con edge& o) Con {return v<o.v;}
    }e[M+5];
    struct P {int x,y;I P(CI a=0,CI b=0):x(a),y(b){}};
    int f[N+5];I int fa(CI x) {return f[x]^x?f[x]=fa(f[x]):x;}//并查集
    int ta,tb;LL ans;I P MST()//最小生成树求出点坐标
    {
    	RI i,x,y,a=0,b=0;LL t=0;for(i=1;i<=n;++i) f[i]=i;//初始化并查集
    	for(sort(e+1,e+m+1),i=1;i<=m;++i)//Kruskal最小生成树
    		(x=fa(e[i].x))^(y=fa(e[i].y))&&(f[y]=x,t+=e[i].v,a+=s[e[i].p].u,b+=s[e[i].p].v);
    	1LL*a*b==ans?a<ta&&(ta=a,tb=b):1LL*a*b<ans&&(ta=a,tb=b,ans=1LL*a*b);return P(a,b);//求出点坐标,更新答案
    }
    I void Solve(Con P& A,Con P& B)//递归求解
    {
    	RI i,a=0,b=0;for(i=1;i<=m;++i) e[i]=edge(i,s[i].x,s[i].y,(A.y-B.y)*s[i].u+(B.x-A.x)*s[i].v);//三角形面积最大的点
    	P C=MST();1LL*(C.x-A.x)*(B.y-A.y)-1LL*(C.y-A.y)*(B.x-A.x)>0&&(Solve(A,C),Solve(C,B),0);//如果能找到左下方的点,就递归做
    }
    int main()
    {
    	RI i;for(scanf("%d%d",&n,&m),ans=1e18,i=1;i<=m;++i)
    		scanf("%d%d%d%d",&s[i].x,&s[i].y,&s[i].u,&s[i].v),++s[i].x,++s[i].y;
    	for(i=1;i<=m;++i) e[i]=edge(i,s[i].x,s[i].y,s[i].u);P A=MST();//离y轴最近点
    	for(i=1;i<=m;++i) e[i]=edge(i,s[i].x,s[i].y,s[i].v);P B=MST();//离x轴最近点
    	return Solve(A,B),printf("%d %d
    ",ta,tb),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    UI自动化测试框架
    Pyse( selenium-api的二次封装)
    Selenium之webdriverAPI
    selenium 基础之定位方式
    html综合应用
    html基础之js操作
    html基础之Dom操作
    (九)Locust 参数化
    (八)Locust 设置断言
    (七)Locust 的类和方法
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2395.html
Copyright © 2011-2022 走看看