题目链接:https://vjudge.net/problem/Gym-101128J
解题思路:
其实我们可以红点变成一个凸包,很显然如果黑点在凸包里面的话,那么就肯定满足存在凸包上的三个点值得他们组成的三角形包含黑点。
然后我们可以以凸包上的一个顶点作为核心点,二分另一个顶点,查看黑点在这两个点形成的向量左边还是右边,不断缩小凸包的范围,最后缩成一个三角形就可以直接求解了。
#include<math.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
int n,top;
struct node
{
ll x,y;
}s[mx],tubeg[mx],w;
//在向量右侧值为负
ll judge(node p1,node p2,node p0)//面积公式判断正负值
{
ll ans = (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y);
return ans;
}
bool cmp(node a,node b)
{
ll c = judge(w,b,a);//极角排序,同角度按距离从小到大排
if(!c) return pow(a.x-w.x,2)+pow(a.y-w.y,2) < pow(b.x-w.x,2)+pow(b.y-w.y,2);
return c < 0;
}
int Graham()
{
tubeg[0] = s[0],tubeg[1] = s[1];
//0和1肯定是凸包上的点
top = 2;
for(int i=2;i<n;i++)
{
while(top>1&&judge(tubeg[top-2],s[i],tubeg[top-1])>=0) top--;//在矢量左侧的点都去掉
tubeg[top++] = s[i];
}
return top;
}
void readtubeg(){
scanf("%d",&n);
int a,b,p = 0;
for(int i=0;i<n;i++){
scanf("%lld%lld",&s[i].x,&s[i].y);
if(s[i].y<s[p].y) p = i;
else if(s[i].y==s[p].y&&s[i].x<s[p].x) p = i;
}
swap(s[0],s[p]),w.x = s[0].x,w.y = s[0].y;
sort(s+1,s+n,cmp);//以最下左那个坐标为参考坐标做极角排序
Graham();
}
bool check(node tmp){
int l = 1,r = top - 1;
while(l<r-1){
int mid = (l+r)>>1;
ll ok = judge(tubeg[0],tubeg[mid],tmp);
if(ok<0) r = mid;
else l = mid;
}
if(judge(tubeg[0],tubeg[l],tmp)<0) return 0;
if(judge(tubeg[l],tubeg[r],tmp)<0) return 0;
if(judge(tubeg[r],tubeg[0],tmp)<0) return 0;
return 1;
}
int main()
{
readtubeg();
int p,ans = 0;
scanf("%d",&p);
node tmp;
for(int i = 1; i <= p; i++){
scanf("%lld%lld",&tmp.x,&tmp.y);
if(check(tmp)) ans++;
}
printf("%d\n",ans);
return 0;
}