HHHOJ #151. 「NOI模拟 #2」Nagisa

计算几何板子题(我才没有拷板子的说……)

众所周知,三角形的重心坐标是\((\frac{x_1+x_2+x_3}{3},\frac{y_1+y_2+y_3}{3})\)

然后我们发现如果我们有一个点集\(P=\{\vec a+\vec b+\vec c|\vec a\in A,\vec b \in B,\vec c\in C\}\),那么就可以直接查询\((3\times x_,3\times y)\)在不在这个点集里得到答案

其实这样的点集在计算几何上是有名字的,就是传说中的闵可夫斯基和

通俗地讲,点集\(A,B\)的闵可夫斯基和就是:从原点向\(A\)内部的每一个点做向量,将\(B\)沿每个向量移动,所有的最终位置的并

然后我们考虑凸包的闵可夫斯基和,手玩一下就会发现它的边是由原来凸包的边构成的

也就是说把两凸包的边极角排序后直接顺次连起来就是闵可夫斯基和,那么我们求的时候直接归并排序即可(凸包已经有序了)

所以这题就做完了,点在凸包内可以直接搞一个点作为原点,然后二分找极角最接近的边判断

坑点1:可能出现三点共线的情况,因此要扔回去再求一遍凸包

坑点2:极角排序的时候注意三点共线

坑点3:如果找到的极角最接近的边已经到最大的一条了,那么请考虑共线的情况

CODE

#include<cstdio>
#include<algorithm>
#define RI register LL
#define CI const LL&
using namespace std;
typedef long long LL;
const LL N=1000005;
struct Point
{
    LL x,y;
    inline Point(LL X=0,LL Y=0)
    {
        x=X; y=Y;
    }
    inline friend bool operator ==(const Point& A,const Point& B)
    {
        return A.x==B.x&&A.y==B.y;
    }
}A[N],B[N],C[N],stack[N*3],t[N*3],p,st; LL n,m,k,q,num;
typedef Point Vector;
inline LL Cross(const Vector& A,const Vector& B)
{
    return A.x*B.y-A.y*B.x;
}
inline LL Dot(const Vector& A,const Vector& B)
{
    return A.x*B.x+A.y*B.y;
}
inline Point operator +(const Point& A,const Point& B)
{
    return Point(A.x+B.x,A.y+B.y);
}
inline Vector operator -(const Point& A,const Point& B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
inline Point operator *(const Point& A,CI v)
{
    return Point(A.x*v,A.y*v);
}
inline bool cmp(const Point& A,const Point& B)
{
    return A.x<B.x||(A.x==B.x&&A.y<B.y);
}
inline bool Ang_cmp(const Vector& A,const Vector& B)
{
    return Cross(A,B)>0||(!Cross(A,B)&&Dot(A,A)<Dot(B,B));
}
class Computation_Geometry
{
	private:
		Vector va[N],vb[N];
    public:
        inline LL ConvexHull(Point *a,int n)
        {
            sort(a+1,a+n+1,cmp); n=unique(a+1,a+n+1)-a-1;
            RI i,top=0; for (i=1;i<=n;++i)
            {
                while (top>1&&Cross(stack[top]-stack[top-1],a[i]-stack[top])<=0) --top;
                stack[++top]=a[i];
            }
            LL temp=top; for (i=n-1;i;--i)
            {
                while (top>temp&&Cross(stack[top]-stack[top-1],a[i]-stack[top])<=0) --top;
                stack[++top]=a[i];
            }
            if (n>1) --top; for (i=1;i<=top;++i) a[i]=stack[i]; return a[top+1]=a[1],top;
        }
        inline LL Minkowski_Sum(Point *a,CI n,Point *b,CI m)
        {
            RI i,tot; for (i=1;i<n;++i) va[i]=a[i+1]-a[i]; va[n]=a[1]-a[n];
            for (i=1;i<m;++i) vb[i]=b[i+1]-b[i]; vb[m]=b[1]-b[m]; t[tot=1]=a[1]+b[1];
            RI pa=1,pb=1; while (pa<=n&&pb<=m) ++tot,t[tot]=t[tot-1]+(Cross(va[pa],vb[pb])>=0?va[pa++]:vb[pb++]);
            while (pa<=n) ++tot,t[tot]=t[tot-1]+va[pa++]; while (pb<=m) ++tot,t[tot]=t[tot-1]+vb[pb++]; return tot;
        }
        inline bool IsPointInConvexHull(Point *a,CI n,const Point& p)
        {
            if (Cross(p,a[1])>0||Cross(a[n],p)>0) return 0;
            LL pos=lower_bound(a+1,a+n+1,p,Ang_cmp)-t-1;
            if (pos==n) return Dot(p,p)<=Dot(a[n],a[n]);
            return Cross(p-a[pos],a[pos%n+1]-a[pos])<=0;
        }
}G;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    RI i; for (scanf("%lld",&n),i=1;i<=n;++i) scanf("%lld%lld",&A[i].x,&A[i].y);
    for (scanf("%lld",&m),i=1;i<=m;++i) scanf("%lld%lld",&B[i].x,&B[i].y);
    for (scanf("%lld",&k),i=1;i<=k;++i) scanf("%lld%lld",&C[i].x,&C[i].y);
    n=G.ConvexHull(A,n); m=G.ConvexHull(B,m); k=G.ConvexHull(C,k);
    num=G.Minkowski_Sum(A,n,B,m); num=G.ConvexHull(t,num);
    num=G.Minkowski_Sum(C,k,t,num); num=G.ConvexHull(t,num);
    //for (i=1;i<=num;++i) printf("%lld %lld\n",t[i].x,t[i].y);
    for (st=t[1],i=1;i<=num;++i) t[i]=t[i]-st; num=G.ConvexHull(t,num);
    for (scanf("%lld",&q),i=1;i<=q;++i)
    scanf("%lld%lld",&p.x,&p.y),puts(G.IsPointInConvexHull(t,num,(p*3)-st)?"YES":"NO");
    return 0;
}

关于闵可夫斯基和,还有一道有关的题目(竟然老早就做掉了233):JSOI2018]战争 还需要一个小小的转换

posted @ 2019-12-22 20:03  空気力学の詩  阅读(178)  评论(0编辑  收藏  举报