bzoj2395 [Balkan 2011]Timeismoney

Posted scx117

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj2395 [Balkan 2011]Timeismoney相关的知识,希望对你有一定的参考价值。

题意:每条边有两个权值a,b,求图的最小二元和乘积生成树(即该树的sum_a*sum_b最小)。

标程:

技术分享图片
 1 #include<bits/stdc++.h>
 2 #define P pair<ll,ll>
 3 #define fir first
 4 #define sec second
 5 using namespace std;
 6 typedef long long ll;
 7 int read()
 8 {
 9    int x=0,f=1;char ch=getchar();
10    while (ch<0||ch>9) {if (ch==-) f=-1;ch=getchar();}
11    while (ch>=0&&ch<=9) x=(x<<1)+(x<<3)+ch-0,ch=getchar();
12    return x*f;
13 }
14 const int N=10005;
15 int f[N],n,m; 
16 ll ans,qa,qb;
17 struct node{ll u,v,a,b,w;}e[N];
18 int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
19 bool cmp_a(const node &A,const node &B){return A.a<B.a;}
20 bool cmp_b(const node &A,const node &B){return A.b<B.b;}
21 bool cmp_w(const node &A,const node &B){return A.w<B.w;}
22 ll chaji(P A,P B,P C){return (B.fir-A.fir)*(C.sec-A.sec)-(B.sec-A.sec)*(C.fir-A.fir);}
23 P mst()
24 {
25     ll s1=0,s2=0;
26     for (int i=1;i<=n;i++) f[i]=i;
27     for (int i=1;i<=m;i++)
28       if (find(e[i].u)!=find(e[i].v))
29         f[find(e[i].u)]=find(e[i].v),s1+=e[i].a,s2+=e[i].b;
30     ll t=s1*s2;
31     if (t<ans) ans=t,qa=s1,qb=s2;
32       else if (t==ans&&s1<qa) qa=s1,qb=s2;
33     return P(s1,s2);
34 }
35 void solve(P A,P B)
36 {
37    for (int i=1;i<=m;i++)
38      e[i].w=(B.fir-A.fir)*e[i].b-(B.sec-A.sec)*e[i].a;
39    sort(e+1,e+m+1,cmp_w);P C=mst();
40     if (chaji(A,B,C)>=0) return;
41     solve(A,C);solve(C,B);
42 }
43 int main()
44 {
45     n=read();m=read();ans=1ll<<60;
46     for (int i=1;i<=m;i++) e[i].u=read()+1,e[i].v=read()+1,e[i].a=read(),e[i].b=read();
47     sort(e+1,e+m+1,cmp_a);P A=mst();
48     sort(e+1,e+m+1,cmp_b);P B=mst();
49     solve(A,B);
50     printf("%lld %lld
",qa,qb);
51    return 0;
52 }
View Code

 

题解:数形结合+凸包

把每棵生成树按照(sum_a,sum_b)映射到坐标系上,基于乘积的反比例函数性质,动态维护下凸包。

具体地,先找到sum_a最小的点A和sum_b最小的点B,连边,在这条线的下方找到一个距离这条直线最远的点C。那么△ABC中的点一定不及三个端点的答案小。继续AC连边和CB连边找下方点,分治。并对端点统计。

难点在于最远点怎么求?转换成三角形面积最大,用叉积表示,其中一条边已知。向量AB叉乘向量AC=(B.x-A.x)*C.y-(B.y-A.y)*C.x-A.y*(B.x-A.x)+A.x*(B.y-A.y)(这是负的,使其最小)前两项和C有关,后两项都已知,重新对边赋权为(B.x-A.x)*e[i].b-(B.y-A.y)*e[i].a,跑mst,即为C点。

最坏复杂度O(nmlogm)。

如果是三元,那么就相当于在平面下找最远点,使得中间体积最大。分割的时候分成三个部分。

以上是关于bzoj2395 [Balkan 2011]Timeismoney的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 1176: [Balkan2007]Mokia

[BZOJ1176][Balkan2007]Mokia

bzoj千题计划144:bzoj1176: [Balkan2007]Mokia

[BZOJ1176][Balkan2007]Mokia cdq+树状数组

BZOJ1336[Balkan2002]Alien最小圆覆盖 随机增量法

BZOJ 2395 Timeismoney