分组赛时学到的最小乘积生成树模型,感觉这个思路非常神,可以说是数形结合的经典问题

由于生成树有两个权值,我们把每个生成树的权值表示成点坐标(sa,sb)

显然我们知道,乘积最小,那么点必然落在下凸壳上

但由于点太多,graham之类要先知道所有点再求凸包的算法就失效了

于是我们使用quickhull算法,这个算法只要知道凸包上的两个点就可以扩展出下一个点,然后不断分治即可扩展出所有点

显然,以a为关键字和以b为关键字的最小生成树一定是凸包上的两个点i,j

根据quickhull算法,下一个凸包上的点就是离直线ij距离最大的点

转化一下,就是与ij垂直的向量点积最小的点

显然我们只要以此为关键字求最小生成树即可

这样我们不断分治下去,直到无法扩展为止,这样就找到了答案

但我好像被卡常了(还是写的太囧……),总之tle了……

  1 type node=record
  2        x,y,w,a,b:longint;
  3      end;
  4      point=record
  5        x,y:longint;
  6      end;
  7 
  8 var e:array[0..10010] of node;
  9     fa:array[0..210] of longint;
 10     i,n,m:longint;
 11     ans,p1,p2:point;
 12 
 13 function getf(x:longint):longint;
 14   begin
 15     if fa[x]<>x then fa[x]:=getf(fa[x]);
 16     exit(fa[x]);
 17   end;
 18 
 19 procedure swap(var a,b:node);
 20   var c:node;
 21   begin
 22     c:=a;
 23     a:=b;
 24     b:=c;
 25   end;
 26 
 27 function cmp(x,y:node):boolean;
 28   begin
 29     if x.w=y.w then exit(x.a<y.a);
 30     exit(x.w<y.w);
 31   end;
 32 
 33 procedure sort(l,r:longint);
 34   var i,j:longint;
 35       x:node;
 36   begin
 37     i:=l;
 38     j:=r;
 39     x:=e[(l+r) shr 1];
 40     repeat
 41       while cmp(e[i],x) do inc(i);
 42       while cmp(x,e[j]) do dec(j);
 43       if not(i>j) then
 44       begin
 45         swap(e[i],e[j]);
 46         inc(i);
 47         dec(j);
 48       end;
 49     until i>j;
 50     if l<j then sort(l,j);
 51     if i<r then sort(i,r);
 52   end;
 53 
 54 function mintree:point;
 55   var i,j,sa,sb,x,y:longint;
 56   begin
 57    {for j:=1 to m do
 58     writeln(e[j].w);  }
 59     j:=0;
 60     sa:=0;
 61     sb:=0;
 62     for i:=1 to n do
 63       fa[i]:=i;
 64     i:=0;
 65     while i<n-1 do
 66     begin
 67       inc(j);
 68       x:=getf(e[j].x);
 69       y:=getf(e[j].y);
 70       if x<>y then
 71       begin
 72         fa[x]:=y;
 73         sa:=sa+e[j].a;
 74         sb:=sb+e[j].b;
 75         inc(i);
 76       end;
 77     end;
 78     mintree.x:=sa;
 79     mintree.y:=sb;
 80     if (int64(ans.x)*int64(ans.y)>int64(sa)*int64(sb)) or (int64(ans.x)*int64(ans.y)=int64(sa)*int64(sb)) and (ans.x>sa) then
 81     begin
 82       ans.x:=sa;
 83       ans.y:=sb;
 84     end;
 85   end;
 86 
 87 function cross(a,b,c:point):int64;
 88   begin
 89     exit(int64(a.x-c.x)*int64(b.y-c.y)-int64(a.y-c.y)*int64(b.x-c.x));
 90   end;
 91 
 92 procedure work(p1,p2:point);
 93   var i:longint;
 94       p:point;
 95 
 96   begin
 97     for i:=1 to m do
 98       e[i].w:=e[i].a*(p1.y-p2.y)+e[i].b*(p2.x-p1.x);
 99     sort(1,m);
100     p:=mintree;
101     if cross(p2,p,p1)>=0 then exit;
102     work(p1,p);
103     work(p,p2);
104   end;
105 
106 begin
107   readln(n,m);
108   for i:=1 to m do
109   begin
110     readln(e[i].x,e[i].y,e[i].a,e[i].b);
111     inc(e[i].x);
112     inc(e[i].y);
113     e[i].w:=e[i].a;
114   end;
115   ans.x:=1000000007;
116   ans.y:=1000000007;
117   sort(1,m);
118   p1:=mintree;
119   for i:=1 to m do
120     e[i].w:=e[i].b;
121   sort(1,m);
122   p2:=mintree;
123   work(p1,p2);
124   writeln(ans.x,' ',ans.y);
125 end.
View Code

 

posted on 2015-06-13 11:28  acphile  阅读(647)  评论(0编辑  收藏  举报