【最小乘积生成树】bzoj2395[Balkan 2011]Timeismoney

 设每个点有x,y两个权值,求一棵生成树,使得sigma(x[i])*sigma(y[i])最小。

 

设每棵生成树为坐标系上的一个点,sigma(x[i])为横坐标,sigma(y[i])为纵坐标。则问题转化为求一个点,使得xy=k最小。即,使过这个点的反比例函数y=k/x最接近坐标轴。

 

Step1:求得分别距x轴和y轴最近的生成树(点):AB(分别按x权值和y权值做最小生成树即可)。

 

Step2:寻找一个在AB的靠近原点一侧的且离AB最远的生成树C,试图更新答案。

 

【怎么找????

——由于CAB最远,所以SABC面积最大。

向量AB=B.x - A.x , B.y - A.y

向量AC= (C.x - A.x , C.y - A.y)

向量ABAC的叉积(的二分之一)SABC的面积(只不过叉积是有向的,是负的,所以最小化这个值,即为最大化面积)。

 

最小化:(B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x)

=(B.x-A.x)*C.y+(A.y-B.y)*C.x  -  A.y*(B.x-A.x)+A.x*(B.y-A.y)/*粗体为常数,不要管*/

 

所以将每个点的权值修改为 y[i]*(B.x-A.x)+(A.y-B.y)*x[i] 做最小生成树,找到的即是C。】

 

Step3:递归地分别往ACBC靠近原点的一侧找。递归边界:该侧没有点了(即叉积大于等于零)

 

BZOJ2395 裸题

Code:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 int res;
 6 char c;
 7 inline int Get()
 8 {
 9     res=0;c='*';
10     while(c<'0'||c>'9')c=getchar();
11     while(c>='0'&&c<='9'){res=res*10+(c-'0');c=getchar();}
12     return res;
13 }
14 struct Edge{int u,v,c,t,w;void read(){u=Get();v=Get();c=Get();t=Get();}};
15 struct Point{int x,y;Point(const int &A,const int &B){x=A;y=B;}Point(){}};
16 typedef Point Vector;
17 typedef long long LL;
18 Vector operator - (const Point &a,const Point &b){return Vector(a.x-b.x,a.y-b.y);}
19 int Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}
20 bool operator < (const Edge &a,const Edge &b){return a.w<b.w;}
21 Edge edges[10001];
22 int n,m,rank[201],fa[201];
23 Point ans=Point(1000000000,1000000000),minc,mint;
24 inline void init()
25 {
26     memset(rank,0,sizeof(rank));
27     for(int i=0;i<n;i++)
28       fa[i]=i;
29 }
30 int findroot(int x) 
31 {
32     if(fa[x]==x)
33       return x;
34     int t=findroot(fa[x]);
35     fa[x]=t;
36     return t;
37 }
38 inline void Union(int U,int V)
39 {
40     if(rank[U]<rank[V])
41       fa[U]=V;
42     else
43       {
44         fa[V]=U;
45         if(rank[U]==rank[V])
46           rank[U]++;
47       }
48 }
49 inline Point Kruscal()
50 {
51     int tot=0;
52     Point now=Point(0,0);
53     init();
54     for(int i=1;i<=m;i++)
55       {
56           int U=findroot(edges[i].u),V=findroot(edges[i].v);
57           if(U!=V)
58             {
59                 Union(U,V);
60                 tot++;
61                 now.x+=edges[i].c;
62                 now.y+=edges[i].t;
63                 if(tot==n-1)
64                   break;
65             }
66       }
67     LL Ans=(LL)ans.x*ans.y,Now=(LL)now.x*now.y;
68     if( Ans>Now || (Ans==Now&&now.x<ans.x) )
69       ans=now;
70     return now;
71 }
72 void Work(Point A,Point B)
73 {
74     for(int i=1;i<=m;i++)
75       edges[i].w=edges[i].t*(B.x-A.x)+edges[i].c*(A.y-B.y);
76     sort(edges+1,edges+m+1);
77     Point C=Kruscal();
78     if(Cross(B-A,C-A)>=0)
79       return;
80     Work(A,C);
81     Work(C,B);
82 }
83 int main()
84 {
85     scanf("%d%d",&n,&m);
86     for(int i=1;i<=m;i++)
87       edges[i].read();
88     for(int i=1;i<=m;i++)
89       edges[i].w=edges[i].c;
90     sort(edges+1,edges+m+1);
91     minc=Kruscal();
92     for(int i=1;i<=m;i++)
93       edges[i].w=edges[i].t;
94     sort(edges+1,edges+m+1);
95     mint=Kruscal();
96     Work(minc,mint);
97     printf("%d %d\n",ans.x,ans.y);
98     return 0;
99 }

 

posted @ 2014-09-06 15:36  AutSky_JadeK  阅读(3623)  评论(1编辑  收藏  举报
TVアニメ「Charlotte(シャーロット)」公式サイト TVアニメ「Charlotte(シャーロット)」公式サイト