题意:
给出n个点和一个面积s,问能否从n个点中选3个点组成s的三角形的面积为s。
题解:
这题我思考了很久,因为确实方法不是很好懂。
首先,原题是bzoj 3707,这道题是给出n个点,求3个点组成的最小面积。
我们先从这题入手。
解法一:分块+暴力
将坐标系多次旋转一个随机角度,将点排序后,由于是求最小值,所以在相邻的点最有可能,可以分块,在块内部暴力求解。
解法二:枚举线段+O(1)找最小值
先将点以x轴坐标为关键字排序,再枚举所有的线段,将线段以斜率为关键字排序。
两个数组:
rk[i]表示第i个点的序号。
pos[i]表示序号为i的点是第几个点。
当我们枚举第一条线段时,他的斜率一定是最小的,而且它的两个端点的序号一定是紧挨着的。
那么,与这条线段形成的面积最小的三角形的点一定满足下列性质:
将线段当做y轴旋转坐标系后,横坐标距离线段最近的点就是面积最小的点。
而rk数组和pos数组,维护的就是一个序列,这个序列满足,序号从小到大,到当前的斜率的直线的距离满足距离递减,可以想象x轴的无穷远处有1条斜率已知的直线。
那么一开始,这个序列就是1-n,因为我们先按x坐标排序了,符合条件。
当判断完第一条线段,要判断第二条线段时,显然这个序列不再满足条件,需要做一些调整。
设第一条线段的两个点为u、v,当判断第二条线段时,画图可以发现,只有u、v两点不满足条件,而且调整方法就是交换u、v两点的序号。
这样,我们知道线段的端点u、v的序号rk[u]、rk[v](rk[u]<rk[v]),那么,序号为rk[u]-1或者rk[v]+1的点与线段形成的面积最小。
这时,pos数组就派上用场,因为刚刚说了需要一个序号到点的映射,可见,pos和rk是互逆的。
回到CF这道题来,根据序列的性质,序号的单调与面积的单调是一致的,所以我们可以用二分查找。
代码:
#include<bits/stdc++.h>
#define N 2001
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lowwer_bound
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
struct Point
{
LL x,y;
Point(LL x=0,LL y=0):x(x),y(y){}
bool operator < (const Point z) const
{
return x<z.x || (x==z.x && y<z.y);
}
Point operator - (const Point z) const
{
return Point(x-z.x,y-z.y);
}
}a[N];
LL Cross(Point z,Point zz)
{
return z.x*zz.y-z.y*zz.x;
}
struct Seg
{
int u,v;
Point p;
}seg[N*N/2];
bool cmp (Seg z,Seg zz)
{
return Cross(z.p,zz.p)>0;
}
int rk[N],pos[N];
int main()
{
int n;LL s;
sc(n); scanf("%lld",&s); s<<=1;
for (int i=1;i<=n;i++) scanf("%lld%lld",&a[i].x,&a[i].y);
sort(a+1,a+n+1);
int cnt=0;
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++) seg[++cnt]=Seg{i,j,a[j]-a[i]};
sort(seg+1,seg+cnt+1,cmp);
for (int i=1;i<=n;i++) rk[i]=pos[i]=i;
for (int i=1;i<=cnt;i++)
{
Point p=seg[i].p;
int u=seg[i].u,v=seg[i].v,l,r;
if (rk[u]>rk[v]) swap(u,v);
l=1; r=rk[u]-1;
while(l<=r)
{
int t=(l+r)>>1;
LL tm=abs(Cross(p,a[pos[t]]-a[pos[rk[u]]]));
if (tm==s)
{
cout<<"Yes\n"<<a[u].x<<' '<<a[u].y<<"\n"<<a[v].x<<' '<<a[v].y<<"\n"<<a[pos[t]].x<<' '<<a[pos[t]].y;
return 0;
}else tm>s?l=t+1:r=t-1;
}
swap(rk[u],rk[v]);
swap(pos[rk[u]],pos[rk[v]]);
}
cout<<"No";
return 0;
}