首先,$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.