zoukankan      html  css  js  c++  java
  • [luogu5540]最小乘积生成树

    假设给定的图为$G=(V,E)$(边用四元组$(x,y,a,b)$来描述),对于其一棵生成树$T=(V,E_{T})$,根据定义代价即为$sum_{(x,y,a,b)in E_{T}}asum_{(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')$,根据凸壳的性质有$yge y'$,即$xyge 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 xle 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 1le 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+1sim 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(nlog 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 } 
    View Code
  • 相关阅读:
    Log4net的一点改进
    SONY的几款秋季新品都还是很不错的
    在VisualStudio 工具箱中隐藏用户控件
    WPF中的DesignMode判断
    SONY的一款Win8平板
    .Net中的不可变集合(Immutable Collection)简介
    C++ 11中几个我比较喜欢的语法(三)
    很好用的谷歌字体以及Gravatar头像一键替换WordPress插件----WP Acceleration for China 插件
    关于微信获取access_token接口,返回值为-1000的问题
    主合同与补充合同的区别有哪些?
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14627951.html
Copyright © 2011-2022 走看看