設每個點有x,y兩個權值,求一棵生成樹,使得sigma(x[i])*sigma(y[i])最小。
設每棵生成樹為坐標系上的一個點,sigma(x[i])為橫坐標,sigma(y[i])為縱坐標。則問題轉化為求一個點,使得xy=k最小。即,使過這個點的反比例函數y=k/x最接近坐標軸。
Step1:求得分別距x軸和y軸最近的生成樹(點):A、B(分別按x權值和y權值做最小生成樹即可)。
Step2:尋找一個在AB的靠近原點一側的且離AB最遠的生成樹C,試圖更新答案。
【怎么找????
——由於C離AB最遠,所以S△ABC面積最大。
向量AB=(B.x - A.x , B.y - A.y)
向量AC= (C.x - A.x , C.y - A.y)
向量AB、AC的叉積(的二分之一)為S△ABC的面積(只不過叉積是有向的,是負的,所以最小化這個值,即為最大化面積)。
最小化:(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:遞歸地分別往AC、BC靠近原點的一側找。遞歸邊界:該側沒有點了(即叉積大於等於零)。
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 }