画框
题解
多简单的一道题呀!
看到这道题,我们应该很容易·想到要把
∑
A
i
,
p
i
\sum A_{i,p_{i}}
∑Ai,pi与
∑
B
i
,
p
i
\sum B_{i,p_{i}}
∑Bi,pi转化一下。毕竟直接处理这两个的积应该是十分麻烦的。
看到积我们想起了什么呢?面积。我们可以建出一个以
∑
A
i
\sum A_{i}
∑Ai与
∑
B
i
\sum B_{i}
∑Bi作为横纵坐标的坐标轴,这样,每种选法都可以表示为这个坐标轴的一个点,而匹配一个就可以看作这上面的一个向量。当前选法的的点围出的面积。
我们可以先求出
∑
A
i
\sum A_{i}
∑Ai与
∑
B
i
\sum B_{i}
∑Bi分别最小的两个点,记作
A
A
A与
B
B
B,很明显,我们的最优的选法一定在这条线段的左侧,而且一定在所有的选法的点关于线段
A
B
AB
AB围出的下凸包上。
关于这个凸包,我们可以先在这个凸包上二分,找出它面积最小的一个点。
但很明显,如果直接求这个点的面积是十分麻烦的,我们考虑对其进行转化。
很明显,当这个点的面积最小的时候,也就是
S
Δ
A
B
C
S_{\Delta}ABC
SΔABC的面积最大的时候。
而
S
Δ
A
B
C
=
∣
A
B
⃗
×
A
C
⃗
2
∣
S_{\Delta}ABC=|\frac{\vec {AB}\times \vec{AC}}{2}|
SΔABC=∣2AB×AC∣。
可得,
∣
A
B
⃗
×
A
C
⃗
2
∣
=
(
x
B
−
x
A
)
(
y
C
−
y
A
)
+
(
y
B
−
y
A
)
(
x
C
−
x
A
)
=
(
x
B
−
x
A
)
y
C
+
(
y
B
−
y
A
)
x
C
+
2
x
A
y
A
−
x
B
y
A
−
y
B
x
A
|\frac{\vec {AB}\times \vec{AC}}{2}|=(x_{B}-x_{A})(y_{C}-y_{A})+(y_{B}-y_{A})(x_{C}-x_{A})=(x_{B}-x_{A})y_{C}+(y_{B}-y_{A})x_{C}+2x_{A}y_{A}-x_{B}y_{A}-y_{B}x_{A}
∣2AB×AC∣=(xB−xA)(yC−yA)+(yB−yA)(xC−xA)=(xB−xA)yC+(yB−yA)xC+2xAyA−xByA−yBxA。
很明显,这个式子中后半部分是定值,我们只需要使得
(
x
B
−
x
A
)
y
C
+
(
y
B
−
y
A
)
x
C
(x_{B}-x_{A})y_{C}+(y_{B}-y_{A})x_{C}
(xB−xA)yC+(yB−yA)xC最大即可。
我们可以将二分图的边权赋值为
(
x
B
−
x
A
)
y
C
+
(
y
B
−
y
A
)
x
C
(x_{B}-x_{A})y_{C}+(y_{B}-y_{A})x_{C}
(xB−xA)yC+(yB−yA)xC即可通过最大匹配来求出
C
C
C,而先前的点
A
A
A与点
B
B
B均可通过二分图最大匹配跑KM来求出,应该是很模板的做法。
总时间复杂度为 O ( n 3 l o g n ) O\left(n^3log\,n\right) O(n3logn),当然如果用dfs写KM就会是更大的时间复杂度了,笔者看很多题解都写的是dfs,虽说笔者写的是bfs,但好像都可以过。
源码
为祭奠misaka与network故将misaka写作mikasa。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 75
#define reg register
typedef long long LL;
const int INF=0x7f7f7f7f;
const LL inf=0x7f7f7f7f7f7f;
template<typename _T>
inline void read(_T &x){
_T f=1;x=0;char s=getchar();
while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int t,n,a[MAXN][MAXN],b[MAXN][MAXN],mp[MAXN][MAXN],pre[MAXN];
int lx[MAXN],ly[MAXN],px[MAXN],py[MAXN],vx[MAXN],vy[MAXN];LL slack[MAXN];
struct point{int x,y;point(){x=y=0;}};
bool operator == (const point x,const point y){return x.x==y.x&&x.y==y.y;}
queue<int> q;
inline bool check(int u){
vy[u]=1;if(py[u]){vx[py[u]]=1;q.push(py[u]);return 0;}
while(u)swap(u,px[py[u]=pre[u]]);return 1;
}
inline void bfs(const int x){
for(reg int i=1;i<=n;++i)vx[i]=vy[i]=0,slack[i]=inf;
while(!q.empty())q.pop();q.push(x);vx[x]=1;
while(1){
while(!q.empty()){
int u=q.front();q.pop();
for(reg int v=1;v<=n;++v){
if(vy[v])continue;int dif=lx[u]+ly[v]-mp[u][v];
if(dif<slack[v]){pre[v]=u;if(dif)slack[v]=dif;else{if(check(v))return ;}}
}
}
LL d=inf;for(reg int i=1;i<=n;++i)if(!vy[i])d=min(slack[i],d);
for(reg int i=1;i<=n;++i){if(vx[i])lx[i]-=d;if(vy[i])ly[i]+=d;else slack[i]-=d;}
for(reg int i=1;i<=n;++i)if(!vy[i]&&!slack[i]&&check(i))return ;
}
}
inline point mikasa(){
for(reg int i=1;i<=n;++i)lx[i]=-INF,ly[i]=px[i]=py[i]=pre[i]=0;
for(reg int i=1;i<=n;++i)for(reg int j=1;j<=n;++j)lx[i]=max(lx[i],mp[i][j]);
for(reg int x=1;x<=n;++x)bfs(x);
point ans;for(reg int i=1;i<=n;++i)ans.x+=a[py[i]][i],ans.y+=b[py[i]][i];
return ans;
}
inline int sakura(const point A,const point B){
for(reg int i=1;i<=n;++i)for(reg int j=1;j<=n;++j)mp[i][j]=(B.y-A.y)*a[i][j]+(A.x-B.x)*b[i][j];
point mid=mikasa();if(A==mid||B==mid)return min(A.x*A.y,B.x*B.y);
return min(sakura(A,mid),sakura(mid,B));
}
signed main(){
read(t);
while(t--){
read(n);
for(reg int i=1;i<=n;++i)for(reg int j=1;j<=n;++j)read(a[i][j]);
for(reg int i=1;i<=n;++i)for(reg int j=1;j<=n;++j)read(b[i][j]);
for(reg int i=1;i<=n;++i)for(reg int j=1;j<=n;++j)mp[i][j]=-a[i][j];point A=mikasa();
for(reg int i=1;i<=n;++i)for(reg int j=1;j<=n;++j)mp[i][j]=-b[i][j];point B=mikasa();
printf("%d\n",sakura(A,B));
}
return 0;
}