BZOJ 3571

耗尽温柔 提交于 2019-11-28 19:46:59

传送门

首先,$N$个配$N$个还带权值,那就显然是用KM了

这道题的思路和最小乘积生成树的思路差不多,可惜BZOJ上一道裸最小乘积生成树被权限了…

考虑把每一种方案抽象成一个点$(sum A,sum B)$,然后把所有点扔到一个坐标系里面,然后答案就是使得一个反比例函数$xcdot y=k$的$k$最小的点

拿KM找出$sum A$最小的点和$sum B$最小的点(带权最小匹配就把边权取反算带权最大匹配),最优解一定优于这两个点(或位于两个点之一),也就是在这两个点连成的线段下方

设最优解为$c$,$sum A$最小的点为$a$,$sum B$最小的点为$b$

易证明$c$一定在下凸壳上

那么题目就成了求解$a$到$b$的下凸壳。那么问题来了,总点数太多,不可能得到所有的点,怎么求下凸壳?

有一个显然的结论,$vec{ab}$下方距离$vec{ab}$最远的点一定在下凸壳上,即使得$SDelta abc$最大的$c$一定在下凸壳上。

$$SDelta abc=frac{vec{ab}timesvec{ac}}{2}$$

展开再合起来能得到 (向量箭头不一样高差评)

$$SDelta abc=frac{vec{ab}timesvec{c}+vec{a}timesvec{b}}{2}$$

后面那坨已知,扔掉,即求$vec{ab}timesvec{c}$的最大值

找到$c$之后,更新答案,$c$不一定是最优解,我们要将下凸壳遍历,因此继续左右分治下去。

分析时间复杂度,由于要将下凸壳全遍历,设下凸壳上的点数为$S$,每次${rm KM}$的复杂度是$Theta(N^3)$,总复杂度是$Theta(SN^3)$。

在实践中,这个算法是很优秀的,但是还是存在很坏的情况……(跑的还没有暴力快)

上面一大坨实际上是ZYK写的


12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
program bzoj_3571;const inf=maxlongint>>1;type point=record x,y:longint; end;var lx,ly,slack,pair:array[1..70]of longint;    vx,vy:array[1..70]of boolean;    w,a,b:array[1..70,1..70]of longint;    t,n,i,j:longint;    pa,pb:point;function (a,b:longint):longint;begin  if a<b then exit(a) else exit(b);end;function find(x:longint):boolean;var t,y:longint;begin  vx[x]:=true;  for y:=1 to n do  begin    if vy[y] then continue;    t:=lx[x]+ly[y]-w[x,y];    if t=0 then    begin      vy[y]:=true;      if (pair[y]=0)or(find(pair[y])) then      begin        pair[y]:=x;        exit(true);      end;    end else if t<slack[y] then slack[y]:=t;  end;  exit(false);end;function KM:point;var i,j,d:longint;begin  fillchar(pair,sizeof(pair),0);  for i:=1 to n do lx[i]:=-inf;  fillchar(ly,sizeof(ly),0);  for i:=1 to n do  for j:=1 to n do if w[i,j]>lx[i] then lx[i]:=w[i,j];  for i:=1 to n do  begin    fillchar(slack,sizeof(slack),$3f);    repeat      fillchar(vx,sizeof(vx),false);      fillchar(vy,sizeof(vy),false);      if find(i) then break;      d:=inf;      for j:=1 to n do if (not vy[j])and(slack[j]<d) then d:=slack[j];      for j:=1 to n do if vx[j] then dec(lx[j],d);      for j:=1 to n do if vy[j] then inc(ly[j],d) else dec(slack[j],d);    until false;  end;  KM.x:=0;  KM.y:=0;  for i:=1 to n do inc(KM.x,a[pair[i],i]);  for i:=1 to n do inc(KM.y,b[pair[i],i]);end;function solve(l,r:point):longint;var t:point;    i,j:longint;begin  for i:=1 to n do  for j:=1 to n do w[i,j]:=a[i,j]*(r.y-l.y)-b[i,j]*(r.x-l.x);  t:=KM;  if ((t.x=l.x)and(t.y=l.y))or((t.x=r.x)and(t.y=r.y)) then exit(min(l.x*l.y,r.x*r.y));  exit(min(solve(l,t),solve(t,r)));end;begin  readln(t);  for t:=1 to t do  begin    readln(n);    for i:=1 to n do    for j:=1 to n do read(a[i,j]);    for i:=1 to n do    for j:=1 to n do read(b[i,j]);    for i:=1 to n do    for j:=1 to n do w[i,j]:=-a[i,j];    pa:=KM;    for i:=1 to n do    for j:=1 to n do w[i,j]:=-b[i,j];    pb:=KM;    writeln(solve(pa,pb));  end;end.

原文:大专栏  BZOJ 3571


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!