把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF1019D】Large Triangle(斜率排序)

题目链接

  • 给定二维平面上 \(n\) 个点,求是否存在三个点组成的三角形面积为 \(m\)
  • \(3\le n\le2\times10^3\)\(1\le m\le2\times10^{18}\)\(-10^9\le x_i,y_i\le 10^9\),保证不存在三点共线

斜率排序+二分

容易想到去枚举一条连线 \(l\) 作为三角形的底,然后判断是否存在一个点到这条连线的距离恰好等于 \(\frac{2m}{|l|}\)

关键在于如何寻找这个点。考虑将这条线段旋成直角坐标系的纵轴,若能让所有点横坐标有序,就可以直接二分。于是问题就变成了如何维护点的顺序。

这时候就有一个非常有趣的性质:以 \(l\) 为纵轴时两点 \(A,B\) 的横坐标大小关系,只取决于原图中 \(k_l\)\(k_{AB}\) 的大小关系。

所以我们只要将所有点两两之间的连线按斜率排遍序,按照斜率从小到大枚举,则任意两点的横坐标大小关系只会变化恰好一次。

初始所有点按原横坐标大小顺序排序,枚举连线的过程中每次交换两端点,再在连线两边分别二分查找即可。

这题很良心地保证了不存在三点共线,免去了一些讨论。

代码:\(O(n^2\log n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 2000
#define LL long long
using namespace std;
int n,a[N+5];LL m;struct P {int id,x,y;}p[N+5];
struct S {int i,j;double k;I bool operator < (Cn S& o) Cn {return k<o.k;}}s[N*N+5];
I bool cmp(Cn P& A,Cn P& B) {return A.x<B.x;}
I void F1(RI l,RI r,Cn P& A,Cn P& B)//在左侧二分查找
{
	RI mid;LL s;W(l<=r) {mid=l+r>>1,s=abs(1LL*(p[mid].x-A.x)*(p[mid].y-B.y)-1LL*(p[mid].x-B.x)*(p[mid].y-A.y));
		if(s==2*m) printf("Yes\n%d %d\n%d %d\n%d %d\n",A.x,A.y,B.x,B.y,p[mid].x,p[mid].y),exit(0);s<2*m?r=mid-1:l=mid+1;}
}
I void F2(RI l,RI r,Cn P& A,Cn P& B)//在右侧二分查找
{
	RI mid;LL s;W(l<=r) {mid=l+r>>1,s=abs(1LL*(p[mid].x-A.x)*(p[mid].y-B.y)-1LL*(p[mid].x-B.x)*(p[mid].y-A.y));
		if(s==2*m) printf("Yes\n%d %d\n%d %d\n%d %d\n",A.x,A.y,B.x,B.y,p[mid].x,p[mid].y),exit(0);s<2*m?l=mid+1:r=mid-1;}
}
int main()
{
	RI i,j,c=0;for(scanf("%d%lld",&n,&m),i=1;i<=n;++i) scanf("%d%d",&p[i].x,&p[i].y);
	for(sort(p+1,p+n+1,cmp),i=1;i<=n;++i) a[p[i].id=i]=i;
	for(i=1;i<=n;++i) for(j=i+1;j<=n;++j) s[++c]=(S){i,j,1.0*(p[j].y-p[i].y)/(p[j].x-p[i].x)};sort(s+1,s+c+1);//记下所有连线排序
	RI mn,mx;for(i=1;i<=c;++i) mn=min(a[s[i].i],a[s[i].j]),mx=max(a[s[i].i],a[s[i].j]),swap(p[mn],p[mx]),//交换枚举到的两点
		swap(a[s[i].i],a[s[i].j]),mn^1&&(F1(1,mn-1,p[a[s[i].i]],p[a[s[i].j]]),0),mx^n&&(F2(mx+1,n,p[a[s[i].i]],p[a[s[i].j]]),0);//在两侧分别二分查找
	return puts("No"),0;
}
posted @ 2021-11-12 16:14  TheLostWeak  阅读(121)  评论(0编辑  收藏  举报