题意:
给你一个
n
n
n个点
m
m
m条边的无向图,每个边有两个权值
a
i
,
b
i
a_i,b_i
ai,bi,让你求出一个
(
∑
i
∈
T
a
i
)
(
∑
i
∈
T
b
i
)
(\sum_{i \in T}a_i)(\sum_{i \in T}b_i)
(∑i∈Tai)(∑i∈Tbi)最小的生成树。
思路:
首先,对于一棵生成树T,我们令
x
T
=
∑
i
∈
T
a
i
,
y
T
=
∑
i
∈
T
b
i
x_T=\sum_{i \in T}a_i,y_T=\sum_{i \in T}b_i
xT=∑i∈Tai,yT=∑i∈Tbi。那么
T
T
T就可以对应平面上面一个点
(
x
T
,
y
T
)
(x_T,y_T)
(xT,yT)。
那么我们要求的就是 x ∗ y x*y x∗y最小的 T T T,很容易发现这一定是在所有点的下凸壳上。
根据定理,下凸壳上的点数量很少,大概是 l n n ! \sqrt {ln{n!}} lnn!个。这网上有很多种说法。
现在考虑怎么找出这个凸壳。
首先, x x x最小的点和 y y y最小的点一定在凸壳上面,这两个都可以用最小生成树来求,分别设为 A , B A,B A,B。
然后,求出距离线段
A
B
AB
AB最远的点,这个点肯定也在凸壳上面。我们用差乘来求距离。令
A
(
x
1
,
y
1
)
,
B
(
x
2
,
y
2
)
A(x_1,y_1),B(x_2,y_2)
A(x1,y1),B(x2,y2),你要求的点是
C
(
x
,
y
)
C(x,y)
C(x,y)。
A
B
→
×
A
C
→
=
(
x
2
−
x
1
)
(
y
−
y
1
)
−
(
x
−
x
1
)
(
y
2
−
y
1
)
\overrightarrow{AB} \times\overrightarrow{AC}=(x_2-x_1)(y-y_1)-(x-x_1)(y_2-y_1)
AB×AC=(x2−x1)(y−y1)−(x−x1)(y2−y1)。就是要求
(
y
1
−
y
2
)
x
+
(
x
2
−
x
1
)
y
(y_1-y_2)x+(x_2-x_1)y
(y1−y2)x+(x2−x1)y的最小值。
很显然这个东西又可以用最小生成树求解,因此这就变成了一个递归求解的过程。
时间复杂度
O
(
n
l
o
g
n
ln
n
!
)
O(nlogn\sqrt{\ln{n!}})
O(nlognlnn!)。
#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){
x=0;int f=0;char s=getchar();
while(!isdigit(s))f|=s=='-',s=getchar();
while(isdigit(s))x=x*10+s-48,s=getchar();
x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){
if(x<0)putchar('-'),x=-x;
do{buf[++cc]=int(x%10);x/=10;}while(x);
while(cc)putchar(buf[cc--]+'0');
}
const int N=210,M=1e4+10;
int n,m;
struct point{
ll x,y;
point(ll xx=0,ll yy=0):x(xx),y(yy){}
point operator-(const point &k)const{
return point(x-k.x,y-k.y);
}
bool operator<(const point &k)const{
if(x*y!=k.x*k.y)return x*y<k.x*k.y;
return x<k.x;
}
void output(){qw(x),putchar(' '),qw(y),puts("");}
};
point ans=point(1e9,1e9);
ll det(point a,point b){
return a.x*b.y-a.y*b.x;
}
ll dot(point a,point b){
return a.x*b.x+a.y*b.y;
}
struct edge{
int x,y;ll w;int id;
}e2[M];
struct edge2{
int x,y;ll w1,w2;
}e[M];
int fa[N];
int findfa(int x){return x==fa[x]?x:fa[x]=findfa(fa[x]);}
bool cmp(edge p1,edge p2){
return p1.w<p2.w;
}
point kruskal(){
sort(e2+1,e2+m+1,cmp);
rep(i,1,n)fa[i]=i;
ll s1=0,s2=0;
rep(i,1,m){
int x=e2[i].x;
int y=e2[i].y;
if(findfa(x)!=findfa(y)){
fa[findfa(x)]=findfa(y);
s1+=e[e2[i].id].w1;
s2+=e[e2[i].id].w2;
}
}
ans=min(ans,point(s1,s2));
return point(s1,s2);
}
void solve(point p1,point p2){
rep(i,1,m){
e2[i].x=e[i].x;
e2[i].y=e[i].y;
e2[i].id=i;
e2[i].w=(p1.y-p2.y)*e[i].w1+(p2.x-p1.x)*e[i].w2;
}
point mid=kruskal();
if(det(p2-p1,mid-p1)>0)return;
if(det(p2-p1,mid-p1)==0&&dot(p1-mid,p2-mid)>=0)return;
solve(p1,mid),solve(mid,p2);
}
void solve(){
qr(n),qr(m);
rep(i,1,m){
qr(e[i].x),qr(e[i].y),qr(e[i].w1),qr(e[i].w2);
e[i].x++,e[i].y++;
}
rep(i,1,m){
e2[i].x=e[i].x;
e2[i].y=e[i].y;
e2[i].id=i;
e2[i].w=e[i].w1;
}
point p1=kruskal();
rep(i,1,m){
e2[i].x=e[i].x;
e2[i].y=e[i].y;
e2[i].id=i;
e2[i].w=e[i].w2;
}
point p2=kruskal();
solve(p1,p2);
ans.output();
}
int main(){
int tt;tt=1;
while(tt--)solve();
return 0;
}