http://www.elijahqi.win/archives/2812
Description
有n个城市(编号从0..n-1),m条公路(双向的),从中选择n-1条边,使得任意的两个城市能够连通,一条边需要的c的费用和t的时间,定义一个方案的权值v=n-1条边的费用和*n-1条边的时间和,你的任务是求一个方案使得v最小
Input
第一行两个整数n,m,接下来每行四个整数a,b,c,t,表示有一条公路从城市a到城市b需要t时间和费用c
Output
【output】timeismoney.out
仅一行两个整数sumc,sumt,(sumc表示使得v最小时的费用和,sumc表示最小的时间和) 如果存在多个解使得sumc*sumt相等,输出sumc最小的
Sample Input
5 7
0 1 161 79
0 2 161 15
0 3 13 153
1 4 142 183
2 4 236 80
3 4 40 241
2 1 65 92
Sample Output
279 501
HINT
【数据规模】
1<=N<=200
1<=m<=10000
0<=a,b<=n-1
0<=t,c<=255
有5%的数据m=n-1
有40%的数据有t=c
对于100%的数据如上所述
非常好的题解:https://www.cnblogs.com/autsky-jadek/p/3959446.html
求sigma(t)*sigma(c)最小 那么将sigma(t)=B,sigma(c)=A
假设有一棵生成树 然后将t,c作为坐标轴上的(x,y)看成一个点
那么 可以看成是选择这些点构成的一个下凸包上的一个点 具体证明:看上方链接
那么 相当于我需要求一个距离A,B这条直线最远的点 假设为点C 可以用叉积来算
即(B.x-A.x)(C.y-A.y)-(C.x-A.x)(B.y-A.y)
然后关键的一步
将这步式子转化为(B.x-A.x)C.y+(A.y-B.y)*C.x-A.y(B.x-A.x)+A.x*(B.y-A.y)
观察到前半部分我们可以算一下比例乘进去继续kruskal递归做下去 后半部分是常数不用管 然后就做完了
#include<cstdio>
#include<algorithm>
#define ll long long
#define M 11000
#define N 220
using namespace std;
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
return x*f;
}
struct node{
int c,t,w,x,y;
}data[M];
struct P{
int x,y;
}point[N],ans;
int fa[N],n,m;
inline bool cmp(const node &a,const node &b){return a.w<b.w;}
inline int find(int x){while(x!=fa[x]) x=fa[x]=fa[fa[x]];return x;}
inline void init(){for (int i=1;i<=n;++i) fa[i]=i;}
inline P kruskal(){
init();int tot=0;P pt=(P){0,0};
for (int i=1;i<=m;++i){
int x=find(data[i].x),y=find(data[i].y);
if (x!=y){pt.x+=data[i].c;pt.y+=data[i].t;fa[x]=y;if (++tot==n-1) break;}
}
ll aa=(ll)ans.x*ans.y,now=(ll)pt.x*pt.y;
if (now<aa||now==aa&&pt.x<ans.x) ans=pt;
return pt;
}
inline void work(P &a,P &b){
for (int i=1;i<=m;++i) data[i].w=data[i].t*(b.x-a.x)+data[i].c*(a.y-b.y);
sort(data+1,data+m+1,cmp);P c=kruskal();
if ((b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y)>=0) return;
work(a,c);work(c,b);
}
int main(){
freopen("bzoj2395.in","r",stdin);
n=read();m=read();ans.x=1e9;ans.y=1e9;
for (int i=1;i<=m;++i){
data[i].x=read()+1;data[i].y=read()+1;data[i ].c=read();data[i].t=read();data[i].w=data[i].c;
}sort(data+1,data+m+1,cmp);P a=kruskal();
for (int i=1;i<=m;++i) data[i].w=data[i].t;
sort(data+1,data+m+1,cmp);P b=kruskal();work(a,b);
printf("%d %d\n",ans.x,ans.y);
return 0;
}