求锐角三角形个数.
统计锐角的个数,设为
A
,钝角和直角的个数,设为
B
每个锐角三角形有三个锐角,每个钝角和直角三角形均贡献两个锐角
所以答案即为
A−2B3
然后题解在对每个点极角排序之后,采用 two pointers的方式来找上下界
比二分好写多了,也很方便,时间复杂度
O(N2logN)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e3+10;
int n;
struct Point
{
ll x,y;
Point(ll xx=0,ll yy=0)
{
x=xx;
y=yy;
}
Point operator -(const Point & a) const
{
return Point(x-a.x,y-a.y);
}
}p[maxn];
ll xmul(const Point &a,const Point &b)
{
return a.x*b.y - a.y*b.x;
}
ll dot(const Point &a,const Point &b)
{
return a.x*b.x+a.y*b.y;
}
bool cmp(const Point &a,const Point &b)
{
if(a.y * b.y <=0)
{
if(a.y >0 || b.y > 0) return a.y < b.y;
if(a.y == 0 && b.y ==0) return a.x < b.x;
}
return xmul(a,b) > 0;
}
vector<Point>vec;
int main()
{
while(~scanf("%d",&n))
{
ll zhidun = 0,rui = 0,ans = 0;
for(int i = 0;i < n;i++)
scanf("%lld %lld",&p[i].x,&p[i].y);
for(int cor = 0;cor < n;cor++)
{
vec.clear();
for(int i = 0;i < n ;i++)
{
if(i != cor)
{
vec.emplace_back(p[i]-p[cor]);
}
}
sort(vec.begin(),vec.end(),cmp);
vec.insert(vec.end(),vec.begin(),vec.end());
int j=0,k=0,r=0;
for(int i=0;i<n-1;i++)
{
while(j<i+n-1&&xmul(vec[i],vec[j])==0&&dot(vec[i],vec[j])>0)
j++;
k=max(k,j);
while(k<i+n-1&&xmul(vec[i],vec[k])>0&&dot(vec[i],vec[k])>0)
k++;
r=max(r,k);
while(r<i+n-1&&xmul(vec[i],vec[r])>0)
r++;
rui += k-j;
zhidun+=r-k;
}
}
ans = (rui-2*zhidun)/3;
printf("%lld\n",ans);
}
return 0;
}
射线法判断点和多边形位置关系
/*
射线法:判断一个点是在多边形内部,边上还是在外部,时间复杂度为O(n);
射线法可以正确用于凹多边形;
射线法是使用最广泛的算法,这是由于相比较其他算法而言,它不但可以正
确使用在凹多边形上,而且不需要考虑精度误差问题。该算法思想是从点出
发向右水平做一条射线,计算该射线与多边形的边的相交点个数,当点不在
多边形边上时,如果是奇数,那么点就一定在多边形内部,否则,在外部。
需要注意的是: 射线法判断点和多边形位置关系,多边形的点要求必须是逆时针描述.
*/
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 2010;
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
struct point
{
double x, y;
point(double x=0, double y=0) : x(x), y(y){}
friend point operator - (const point& p1, const point& p2)
{
return point(p1.x-p2.x, p1.y-p2.y);
}
friend double operator ^ (const point& p1, const point& p2)
{
return p1.x*p2.y - p1.y*p2.x;
}
};
struct Segment
{
point s, e;
};
///判断一个double类型的数是 0 <0 >0;
int Sign(double x)
{
if( fabs(x) < eps )return 0;
if(x > 0)return 1;
return -1;
}
///判断o在ab的哪边;0:o在直线ab上; >0:在左边; <0:在右边;
double cross(point o, point a, point b)
{
return ((a-o)^(b-o));
}
///已知abc三点在一条直线上,判断点a是否在线段bc之间;<=0:在 >0:不在;
int Between(point a, point b, point c)
{
if(fabs(b.x-c.x) > fabs(b.y-c.y))
return Sign(min(b.x, c.x)-a.x)*Sign(max(b.x, c.x)-a.x);
else
return Sign(min(b.y, c.y)-a.y)*Sign(max(b.y, c.y)-a.y);
}
///判断点p0和线段S上,<=0:在,1:不在;
int PointOnSegment(point p0, Segment S)
{
if(Sign(cross(S.s, S.e, p0)) == 0)
return Between(p0, S.s, S.e);
return 1;
}
///求线段a和线段b的交点个数;
int SegmentCross(Segment a, Segment b)
{
double x1 = cross(a.s, a.e, b.s);
double x2 = cross(a.s, a.e, b.e);
double x3 = cross(b.s, b.e, a.s);
double x4 = cross(b.s, b.e, a.e);
if(Sign(x1*x2)<0 && Sign(x3*x4)<0) return 1;
if((Sign(x1)==0 && Between(b.s, a.s, a.e)<=0) ||
(Sign(x2)==0 && Between(b.e, a.s, a.e)<=0) ||
(Sign(x3)==0 && Between(a.s, b.s, b.e)<=0) ||
(Sign(x4)==0 && Between(a.e, b.s, b.e)<=0))
return 2;
return 0;
}
///判断点p0与含有n个节点的多边形的位置关系
//p数组是顶点集合; 射线法判断点和多边形位置关系,多边形的点要求必须是逆时针描述.
///返回0:边上或顶点上, 1:外面, -1:里面;
int PointInPolygon(point p0, point p[], int n)
{
Segment L, S;
point temp;
L.s = p0, L.e = point(INF, p0.y);///以p0为起点的射线L;
int counts = 0;
p[n] = p[0];
for(int i=1; i<=n; i++)
{
S.s = p[i-1], S.e = p[i];
if(PointOnSegment(p0, S) <= 0) return 0;
if(S.s.y == S.e.y) continue;///和射线平行;
if(S.s.y > S.e.y) temp = S.s;
else temp = S.e;
if(PointOnSegment(temp, L) == -1)
counts ++;
else if(SegmentCross(L, S) == 1)
counts ++;
}
if(counts%2) return -1;
return 1;
}
int main()
{
point p[N];
int T, tCase = 1, n, q;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i=0; i<n; i++)
scanf("%lf %lf", &p[i].x, &p[i].y);
scanf("%d", &q);
printf("Case %d:\n", tCase++);
for(int i=1; i<=q; i++)
{
int x, y;
scanf("%d %d", &x, &y);
int ans = PointInPolygon(point(x, y), p, n);
if(ans == 1) puts("No");
else puts("Yes");
}
}
return 0;
}
凸包
/*
采用Graham扫描算法,复杂度nlogn.
栈中保存点的顺序是按照逆时针顺序
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const int MAXN = 50000 + 5;
struct P{ int x, y; };
P p[MAXN];
int n;
//求两点之间的距离
int dis(P a, P b)
{
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
/*计算叉积
判断点和直线的方向用叉积
若P1×p2 >0 则p1在p2的顺时针方向.
<0 p1在p2的逆时针方向
= 0 共线 */
int cross(P a, P b, P c)
{
return (a.x - c.x) * (b.y - c.y) - (b.x - c.x) * (a.y - c.y);
}
//极角排序
bool cmp(P a, P b)
{
int x = cross(p[0],a,b);
if(x > 0 || (x == 0 && dis(a, p[0]) < dis(b, p[0]))) return 1;
return 0;
}
vector<P> Graham()
{
vector<P> stk(n);//存凸包
int k = 0;
//Graham 算法
for (int i = 0; i < n; i++) {
while (k > 1 && (cross(p[i], stk[k-2], stk[k-1])) <= 0)
k--;
stk[k++] = p[i];
}
stk.resize(k);//调整正确的凸包顶点数
return stk;
}
int main()
{
while (~scanf("%d", &n)) {
int ans = 0, rp = 0;//rp用于临时保存最左下点的下标
//输入
for (int i = 0; i < n; i++) {
scanf("%d%d", &p[i].x, &p[i].y);
if(p[rp].y > p[i].y
|| (p[rp].y == p[i].y && p[rp].x > p[i].x))//找最左下的点
rp = i;
}
//极角排序
swap(p[rp], p[0]);//初始化,方便cmp过程
sort(p + 1, p + n, cmp);
//Graham Scan构成凸包
vector<P> stk = Graham();
//暴力求解
for (int i = 0; i < stk.size(); i++) {
for (int j = 0; j < i; j++)
ans = max(ans, dis(stk[i], stk[j]));
}
cout<<ans<<endl;
}
return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=5e4+5;
struct node
{
int x,y;
}a[maxn],sta[maxn];
//求亮点之间距离的平方.
int dis(node n1,node n2)
{
return (n1.x-n2.x)*(n1.x-n2.x)+(n1.y-n2.y)*(n1.y-n2.y);
}
//计算叉积
int det(int x1,int y1,int x2,int y2)
{
return x1*y2-x2*y1;
}
//判断点和直线的方向用叉积
//若P1×p2 >0 则p1在p2的顺时针方向.
//<0 p1在p2的逆时针方向
//= 0 共线
int cross(node a,node n1,node n2)
{
return det(n1.x-a.x,n1.y-a.y,n2.x-a.x,n2.y-a.y);
}
//极角排序
bool cmp(node n1,node n2)
{
//a[0]为纵坐标最小的点一定在凸包上,以它为原点进行极角排序
int k = cross(a[0],n1,n2);
if(k > 0) return 1;//角度小的排在前面
//若共线则距离a[0]距离近的在前面.
if(k == 0 && dis(a[0],n1) < dis(a[0],n2))
return 1;
return 0;
}
void solve(node *ch,int len)
{
int ans = 0;
for(int i = 0;i <= len;++i)
{
for(int j = i + 1;j <= len;++j)
{
ans = max(ans,dis(ch[i],ch[j]));
}
}
cout<<ans<<endl;
}
//最后栈中存的点就是凸包上的点.
void Graham(int n)
{
int i,head;
for(i = 1;i < n;++i) //先找到最下面的那个点作为原点.
if(a[i].x < a[0].x || (a[i].x == a[0].x && a[i].y < a[0].y))
swap(a[0],a[i]);
sort(a+1,a+n,cmp);//极角排序
a[n] = a[0];
sta[0] = a[0];//最下面的一个点和第二个点一定在凸包上.
sta[1] = a[1];
sta[2] = a[2];
head = 2;
//Graham 算法
for(int i = 3;i < n;++i)
{
while(head >= 2)
{
int fu = cross(sta[head - 1],sta[head],a[i]);
if(fu < 0)
head--;
else if(fu == 0 && dis(sta[head - 1],a[i]) > dis(sta[head - 1],sta[head]))
head--;
else
break;
}
sta[++head] = a[i];
}
solve(sta,head);
}
int main(){
int n;
while(scanf("%d",&n) > 0)
{
for(int i = 0;i < n;++i)
{
scanf("%d %d",&a[i].x,&a[i].y);
}
Graham(n);
}
return 0;
}
叉积求多边形面积
/*
叉积求多边形面积.(凹凸都适用)
要求所有顶点按照逆时针顺序排列(点和多边形、凸包都是逆时针)
由于叉积可能为负所以要取绝对值。
因为每次除有误差,记得全部求完面积在除以2.
*/
double get_area(node *p,int len)
{
double ans = 0;
for(int i = 1;i < len;++i)
{
ans += fabs(cross(p[0],p[i],p[i+1]));
}
return ans*0.5;
}