#2395 [Balkan 2011]Timeismoney
SOL
最小乘积生成树的模板题
设
k
=
x
∗
y
k=x*y
k=x∗y,那么使
k
k
k越大,函数
y
=
k
/
x
y=k/x
y=k/x在第一象限的一支就要越靠近坐标轴
我们先将按
x
x
x排序和按
y
y
y排序得到的答案求出来,在坐标轴上就是最靠近
x
x
x轴和
y
y
y轴的点
显然这不一定是最优的情况
于是要尝试在这2个点上进行扩展
显然更优的决策一定是出现在AB的左侧
设我们已经找到了一个点C(这是个不确定的点)
这时候我们可以发现S△ABC= AB X AC /2的 相反数
而S△ABC面积最大的时候,即为函数图像最靠近坐标轴的时候,所以要最小化 AB X AC
即最小化:
(
B
.
x
−
A
.
x
,
B
.
y
−
A
.
y
)
X
(
C
.
x
−
A
.
x
,
C
.
y
−
A
.
y
)
(B.x-A.x,B.y -A.y) X (C.x-A.x,C.y-A.y)
(B.x−A.x,B.y−A.y)X(C.x−A.x,C.y−A.y)
化简之后,抛开常数我们还有:
C
.
y
∗
(
B
.
x
−
A
.
x
)
−
C
.
x
(
B
.
y
−
A
.
y
)
C.y*(B.x-A.x)-C.x(B.y-A.y)
C.y∗(B.x−A.x)−C.x(B.y−A.y)
所以我们可以利用这个性质维护答案,按
y
[
i
]
∗
(
B
.
x
−
A
.
x
)
−
x
[
i
]
∗
(
B
.
y
−
A
.
y
)
y[i]*(B.x-A.x)-x[i]*(B.y-A.y)
y[i]∗(B.x−A.x)−x[i]∗(B.y−A.y)排序
处理完当前的A,B之后,我们就可以得到一个新的点C,再用A,C和C,B分别进行扩展,继续逼近坐标轴,直到 AB X AC=0,因为这时候C与A或者B重合,不能再扩展了
代码:
#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
inline int rd(){
int re data=0;static char ch=0;ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=getchar();
return data;
}
const int N=205,M=1e4+5;
struct node{int u,v,x,y,c;}e[M];
struct pt{int x,y;}a,b,ans;
inline bool cmp(const node&a,const node&b){return a.c<b.c;}
int n,m,fa[N],eg;
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline pt kruskal(pt re ret=(pt){0,0}){
for(int re i=1;i<=n;++i)fa[i]=i;
eg=0,sort(e+1,e+m+1,cmp);
for(int re i=1;i<=m;++i){
int re fu=find(e[i].u),fv=find(e[i].v);
if(fu==fv)continue;
fa[fu]=fv,++eg,ret.x+=e[i].x,ret.y+=e[i].y;
if(eg==n-1)break;
}
int re sans=ans.x*ans.y,sret=ret.x*ret.y;
if(sans>sret||(sans==sret&&ret.x<ans.x))ans=ret;
return ret;
}
inline pt del(const pt&a,const pt&b){return (pt){a.x-b.x,a.y-b.y};}
inline int cross(const pt&a,const pt&b){return a.x*b.y-a.y*b.x;}
inline void work(const pt&a,const pt&b){
for(int re i=1;i<=m;++i)e[i].c=e[i].y*(b.x-a.x)-e[i].x*(b.y-a.y);
pt re c=kruskal();
if(cross(del(b,a),del(c,a))==0)return;
work(a,c),work(c,b);
}
signed main(){
n=rd(),m=rd(),ans.x=(int)1e9,ans.y=(int)1e9;
for(int re i=1;i<=m;++i)e[i]=(node){rd()+1,rd()+1,rd(),rd()};
for(int re i=1;i<=m;++i)e[i].c=e[i].x;a=kruskal();
for(int re i=1;i<=m;++i)e[i].c=e[i].y;b=kruskal();
work(a,b),printf("%lld %lld",ans.x,ans.y),exit(0);
}