[HNOI2014]画框

画框

题解

多简单的一道题呀!

看到这道题,我们应该很容易·想到要把 ∑ 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 =(xBxA)(yCyA)+(yByA)(xCxA)=(xBxA)yC+(yByA)xC+2xAyAxByAyBxA
很明显,这个式子中后半部分是定值,我们只需要使得 ( x B − x A ) y C + ( y B − y A ) x C (x_{B}-x_{A})y_{C}+(y_{B}-y_{A})x_{C} (xBxA)yC+(yByA)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} (xBxA)yC+(yByA)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),当然如果用dfsKM就会是更大的时间复杂度了,笔者看很多题解都写的是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;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值