画框
题解
多简单的一道题呀!
看到这道题,我们应该很容易·想到要把 ∑ 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;
}