[luogu5540]最小乘积生成树
假设给定的图为$G=(V,E)$(边用四元组$(x,y,a,b)$来描述),对于其一棵生成树$T=(V,E_{T})$,根据定义代价即为$\sum_{(x,y,a,b)\in E_{T}}a\sum_{(x,y,a,b)\in E_{T}}b$
考虑构造一个二维平面,生成树$T$对应于平面上的一个点$(\sum_{(x,y,a,b)\in E_{T}}a,\sum_{(x,y,a,b)\in E_{T}}b)$,令这些点构成集合$S$,我们即求$\min_{(x,y)\in S}xy$
对$S$求左下凸壳,构成集合$S'$(删去$S'$中不必要的点,即不允许三点共线)
结论:$\min_{(x,y)\in S}xy=\min_{(x,y)\in S'}xy$
考虑$(x,y)\in S$,选择$S'$中$x$坐标小于等于$x$且最大的点$(x_{1},y_{1})$和大于等于$x$且最小的点$(x_{2},y_{2})$,将两点连线后经过点$(x,y')$,根据凸壳的性质有$y\ge y'$,即$xy\ge xy'$
下面,考虑来证明$xy'\ge \min(x_{1}y_{1},x_{2}y_{2})$即可
由于$(x,y')$在两点连线上,有$y'=y_{1}+\frac{y_{2}-y_{1}}{x_{2}-x_{1}}(x-x_{1})$,代入即$xy'=\frac{y_{2}-y_{1}}{x_{2}-x_{1}}x^{2}+(y_{1}-\frac{y_{2}-y_{1}}{x_{2}-x_{1}}x_{1})x$
注意到$\frac{y_{2}-y_{1}}{x_{2}-x_{1}}<0$,又因为$x_{1}\le x\le x_{2}$,显然其最小值在$x=x_{1}$或$x=x_{2}$时取到,代入可得$y$恰为$y_{1}$和$y_{2}$,即其最小值为$\min(x_{1}y_{1},x_{2}y_{2})$,得证
下面来求这个凸壳$S'$,考虑递归来求,具体即——
假设已经求出$(x_{1},y_{1}),(x_{2},y_{2})\in S'$(不妨假设$x_{1}<x_{2}$),找到任意一点$(x_{3},y_{3})$满足$x_{1}<x_{3}<x_{2}$且$(x_{3},y_{3})\in S'$,并分别递归$(x_{1},y_{1})$和$(x_{3},y_{3})$、$(x_{3},y_{3})$和$(x_{2},y_{2})$即可
简单贪心即可求出凸壳上$x$坐标最小和最大($y$坐标最小)的两点,以此为基础进行递归
下面考虑如何求$(x_{3},y_{3})$,构造$(x_{3},y_{3})$是$S$中到$(x_{1},y_{1})$和$(x_{2},y_{2})$连线上距离最远的点(这里的距离是有方向的,在其连线下方为正、上方为负),距离相同时取$x_{3}$最小
特别的,当此距离小于等于0,即不存在满足条件的$(x_{3},y_{3})$,结束即可
关于这一做法的正确性证明,只需要说明以下两点:
1.若该点到$(x_{1},y_{1})$和$(x_{2},y_{2})$连线上距离小于等于0,不存在满足条件的$(x_{3},y_{3})$
2.若该点到$(x_{1},y_{1})$和$(x_{2},y_{2})$连线上距离大于0,其满足$x_{1}<x_{3}<x_{2}$且$(x_{3},y_{3})\in S'$
根据凸壳的性质,都是可以说明的
同时,最大化此距离也即最小化过$(x_{3},y_{3})$作斜率为$\frac{y_{2}-y_{1}}{x_{2}-x_{1}}$的直线的截距,也即$y_{3}+\frac{y_{2}-y_{1}}{x_{2}-x_{1}}x_{3}$
联系最开始的定义,以$\frac{y_{2}-y_{1}}{x_{2}-x_{1}}a+b$为边权求最小生成树即可(在$\frac{y_{2}-y_{1}}{x_{2}-x_{1}}a+b$相同时由于希望$x_{3}$尽量小,按$a$从小到大排序),(求最小生成树)复杂度为$o(n^{2}+m)$
由此,我们就得到了一个$o(|S'|(n^{2}+m)$的做法,来考虑$|S'|$
注意到$S$中的点都是整点,且范围在$[0,nV]$中(其中$V$为$a$和$b$的范围,记$N=nV$)
假设其有$k$段直线,第$i$条直线斜率为$-\frac{p_{i}}{q_{i}}$(其中$p_{i},q_{i}\ge 1$且$(p_{i},q_{i})=1$),即有$\forall 1\le i<k,\frac{p_{i}}{q_{i}}>\frac{p_{i+1}}{q_{i+1}}$(凸性),且需要能在$[0,N]$中放得下,即$\sum_{i=1}^{k}p_{i},\sum_{i=1}^{k}q_{i}\le N$
对于其中$\max(p_{i},q_{i})\le N^{\frac{1}{3}}$的位置,由于不会重复,至多有$o(N^{\frac{2}{3}})$个数
对于其中$\max(p_{i},q_{i})>N^{\frac{1}{3}}$的位置,根据和不超过$N$的条件,也至多有$o(N^{\frac{2}{3}})$个位置
综上,我们就说明了$|S'|=k+1\sim o(N^{\frac{2}{3}})$,总复杂度即$o(n^{\frac{8}{3}}V^{\frac{2}{3}}+n^{\frac{2}{3}}V^{\frac{2}{3}}m)$
另外顺带一提,在数据随机时,对于$n$个点的凸包期望点数为$o(\log n)$,由于至多只有$o(n^{n-2})$棵生成树(点),因此$|S'|$的期望是$o(n\log n)$的,总复杂度为$o(n^{3}\log n)$(虽然会被卡,其实比较困难)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 205 4 #define pii pair<int,int> 5 #define fi first 6 #define se second 7 vector<pii >v[N][N]; 8 pii ans,d[N]; 9 int n,m,x,y,a,b,vis[N]; 10 double tmp; 11 bool cmp(pii x,pii y){ 12 return tmp*x.fi+x.se<tmp*y.fi+y.se; 13 } 14 void get_min(pii k){ 15 if (1LL*k.fi*k.se<1LL*ans.fi*ans.se)ans=k; 16 if ((1LL*k.fi*k.se==1LL*ans.fi*ans.se)&&(k.fi<ans.fi))ans=k; 17 } 18 pii calc(double k){ 19 tmp=k; 20 pii ans=make_pair(0,0); 21 memset(vis,0,sizeof(vis)); 22 d[1]=make_pair(0,0); 23 for(int i=2;i<=n;i++)d[i]=make_pair(0x3f3f3f3f,0x3f3f3f3f); 24 for(int i=1;i<=n;i++){ 25 int k=-1; 26 for(int j=1;j<=n;j++) 27 if ((!vis[j])&&((k<0)||(cmp(d[j],d[k]))))k=j; 28 vis[k]=1; 29 ans.fi+=d[k].fi,ans.se+=d[k].se; 30 for(int j=1;j<=n;j++) 31 for(int t=0;t<v[k][j].size();t++) 32 if (cmp(v[k][j][t],d[j]))d[j]=v[k][j][t]; 33 } 34 get_min(ans); 35 return ans; 36 } 37 void dfs(pii x,pii y){ 38 if (x.fi==y.fi)return; 39 pii o=calc(1.0*(y.se-x.se)/(x.fi-y.fi)); 40 if (!cmp(o,x))return; 41 dfs(x,o),dfs(o,y); 42 } 43 int main(){ 44 scanf("%d%d",&n,&m); 45 for(int i=1;i<=m;i++){ 46 scanf("%d%d%d%d",&x,&y,&a,&b); 47 x++,y++; 48 v[x][y].push_back(make_pair(a,b)); 49 v[y][x].push_back(make_pair(a,b)); 50 } 51 ans.fi=ans.se=1e5; 52 dfs(calc(1e5),calc(0)); 53 printf("%d %d",ans.fi,ans.se); 54 }