Large Triangle
给出n个点的坐标,求由其中三个点组成的三角形里面有没有面积等于s的
在网上找题解,可以暴力+hash
不够更多的做法是用旋转坐标系+二分
做这道题时先看了一道叫做圈地的题,是求点组成的面积最小的三角形,看了很久才稍微懂了一点,先将点按照x坐标排序,然后每两点构成的线段按照斜率排序,在处理某一条线段ab时,由于各个点按照x坐标排了序,理论上按照顺序在a、b两边的点就是离线段ab最近的点,那么以ab为y轴时,高就是ab,而底就是点与y轴的距离,那么将离y轴最近的点作为三角形的第三个点,这个三角形就是以ab为高时所能求出的最小面积的三角形,而在新坐标系中,只有a、b相对位置会发生变化,因此,每次处理完一条线段,就交换两点顺序(说实话,这点是我最不能想通的地方,只好由结论反推,具体看代码注释),只要枚举所有线段,就能求出最小面积的三角形了
在圈地基础上,分别在a、b两端二分查找,找出使得面积刚好为s的点,枚举所有线段
#include<stdio.h>
#include<vector>
#include<algorithm>
#include<string.h>
#include<iostream>
#include<fstream>
#include<math.h>
#include<stack>
#include<bitset>
#include<utility>
using namespace std;
typedef long long ll;
const double eps=0.0000000000001;
const int mod=1000000007;
int n;
ll s;
bool f;
struct P{
ll x;
ll y;
};
P p[2005];
struct D{
int i;
int j;
double w;
};
D dt[4000025];
int id[2005];
int pos[2005];
ll cross(P b,P a,P c){
return fabs((ll)(b.x-a.x)*(c.y-a.y)-(ll)(b.y-a.y)*(c.x-a.x));
}
bool cmp(D a,D b){
return a.w<b.w;
}
bool cmpp(P a,P b){
if(a.x==b.x)return a.y<b.y;
return a.x<b.x;
}
void print(P a,P b,P c){
printf("Yes\n");
printf("%I64d %I64d\n",a.x,a.y);
printf("%I64d %I64d\n",b.x,b.y);
printf("%I64d %I64d\n",c.x,c.y);
}
int main(){
scanf("%d%I64d",&n,&s);
ll x,y;
for(int i=0;i<n;i++){
scanf("%I64d%I64d",&x,&y);
p[i].x=x;
p[i].y=y;
}
sort(p,p+n,cmpp);//点以x坐标为第一关键字,y为第二关键字排序
int k=0;
for(int i=0;i<n-1;i++){
for(int j=i+1;j<n;j++){
dt[k].i=i;
dt[k].j=j;
dt[k].w=(double)(p[j].y-p[i].y)/(p[j].x-p[i].x);
//a/b为double时,若b=0,a/b=inf
k++;
}
}
//dt[k]中存放线段两个端点坐标和斜率
sort(dt,dt+k,cmp);//所有线段按斜率排序
for(int i=0;i<n;i++){
pos[i]=id[i]=i;
}
//pos中存放各个点位置,id中存放每个位置对应点的id
//处理完一条线段之后点的位置会更新,pos中的点在新的坐标系中顺序和在x轴上顺序相同
for(int i=0;i<k;i++){
int x=dt[i].i;
int y=dt[i].j;
if(pos[x]>pos[y])swap(x,y);
int l=0,r=pos[x]-1;
while(l<=r){
int mid=(l+r)/2;
ll w=cross(p[id[mid]],p[x],p[y]);
if(w==s*2){
print(p[id[mid]],p[x],p[y]);
return 0;
}
else if(w<s*2){
r=mid-1;//在线段xy左边的点,由于以xy为y轴,越大离xy越近,面积越小
}
else l=mid+1;
}
l=pos[y]+1,r=n-1;
while(l<=r){
int mid=(l+r)/2;
ll w=cross(p[id[mid]],p[x],p[y]);
if(w==s*2){
print(p[id[mid]],p[x],p[y]);
return 0;
}
else if(w<s*2){
l=mid+1;
}
else r=mid-1;
}
//以线段xy为y轴分别在左右两边二分查找
swap(pos[x],pos[y]);
swap(id[pos[x]],id[pos[y]]);
/*在以x(或y)为其中一个端点,且斜率更高的直线xz为y轴为新坐标系的图中
若之前点z与直线xy距离(x轴方向上)是d,将x和y距离看为0,则点的顺序为:x y z
而在新坐标系中,x与z距离为0,y与xz距离为d,点顺序变为:y x z
所以每次旋转坐标系时,只有x和y相对位置会发生变化
所以每处理完一条直线则交换两点顺序
*/
}
printf("No\n");
return 0;
}