1. 程式人生 > >bzoj 2395 [Balkan 2011]Timeismoney——最小乘積生成樹

bzoj 2395 [Balkan 2011]Timeismoney——最小乘積生成樹

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=2395

如果把 \( \sum t \) 作為 x 座標,\( \sum c \) 作為 y 座標,則每棵生成樹都是二維平面上的一個點。

答案是二維平面上的一個下凸殼。先求出只考慮 t 的最小生成樹和只考慮 c 的最小生成樹,它們就是凸殼的兩端。

已知兩端,考慮遞迴下去,則要找到距離這兩端構成的直線最遠的點。

這就是點到直線的距離,等價於三個點組成的三角形面積最小;考慮叉積公式,得出面積關於要找的點的 x , y 座標的式子,形如 A*x + B*y ;

給邊權乘上係數,就能求最小生成樹得到該點;如果面積是負的,就求最小生成樹,否則求最大生成樹。

判斷是否不用再往下遞迴,本來寫的是找到的那個點就是兩端點之一,結果T了;寫成找到的那個點在兩端點的連線上就可以了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=205,M=1e4+5;
int n,m,fa[N],dep[N];
struct Ed{int t,c,w,x,y;}ed[M];
struct Node{
  int t,c;ll w;
  Node(){t=c=w=0
;} bool operator== (const Node &b)const {return t==b.t&&c==b.c;} }ans; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } void frh(Node p){if
(p.w<ans.w||(p.w==ans.w&&p.c<ans.c))ans=p;} bool cmp(Ed u,Ed v){return u.w<v.w;} int fnd(int a){return fa[a]==a?a:fa[a]=fnd(fa[a]);} ll Cross(int x1,int y1,int x2,int y2) {return (ll)x1*y2-(ll)x2*y1;} Node calc(int t0,int t1) { for(int i=1;i<=m;i++)ed[i].w=(ll)t0*ed[i].t+(ll)t1*ed[i].c; sort(ed+1,ed+m+1,cmp); memset(dep,0,sizeof dep); for(int i=1;i<=n;i++)fa[i]=i; Node ret; for(int i=1,u,v,cnt=0;i<=m;i++) { if((u=fnd(ed[i].x))==(v=fnd(ed[i].y)))continue; if(dep[u]>dep[v])swap(u,v); fa[u]=v;if(dep[u]==dep[v])dep[v]++; ret.t+=ed[i].t;ret.c+=ed[i].c; cnt++;if(cnt==n-1)break; } ret.w=(ll)ret.t*ret.c; return ret; } void solve(Node p0,Node p1) { int st=p1.c-p0.c,sc=p0.t-p1.t; Node res=calc(st,sc);frh(res); // if(res==p0||res==p1)return; if(Cross(p1.t-res.t,p1.c-res.c,p0.t-res.t,p0.c-res.c)>=0)return; solve(p0,res); solve(res,p1); } int main() { n=rdn();m=rdn(); for(int i=1;i<=m;i++) ed[i].x=rdn()+1,ed[i].y=rdn()+1,ed[i].t=rdn(),ed[i].c=rdn(); ans.t=ans.c=1e9;ans.w=1e18; Node p0=calc(0,1),p1=calc(1,0); frh(p0);frh(p1); solve(p0,p1); printf("%d %d\n",ans.t,ans.c); return 0; }