其他分享
首页 > 其他分享> > 【题解】Mogohu-Rea Idol [CF87E]

【题解】Mogohu-Rea Idol [CF87E]

作者:互联网

【题解】Mogohu-Rea Idol [CF87E]

传送门:\(\text{Mogohu-Rea Idol}\) \(\text{[CF87E]}\)

【题目描述】

按逆时针顺序给出三个凸包点集 \(A,B,C\),每次查询给出点 \(q\),问是否存在点 \(a\in A,b\in B,c\in C\) 满足 \(q\) 为 \(\Delta abc\) 的重心。

【分析】

【学习笔记】计算几何全家桶

目前我所知的第三道闵可夫斯基和的题(貌似是个冷门老题)

考虑三角形重心 \(Q\) 的基本性质:\(\vec{OA}+\vec{OB}+\vec{OC}=3\vec{OQ}\) 。

然后就是个板子题了...

对三个凸包求个和,得到 \(P=\{a+b+c|a\in A,b\in B,c\in C\}\),然后直接判断点 \(q*3\) 是否在 \(P\) 以内即可。

注意题目给出的虽然是严格凸包,但还是要自己写个算法跑一下,目的是将坐标最小的放在第一个位置,否则后面对凸包求和时会出问题(也可以直接枚举找最小点作为凸包起点)。

然后就是做完闵可夫斯基和后要再对其求一下凸包,目的是去掉平行边(或者说重边。在 \(P\) 上表现为三点共线)。

时间复杂度:\(O((n+m)\log n)\) 。

其实可以把两部分求凸包的过程都换掉:前面枚举找最小点、多加一个特判处理重边,就变成了 \(O(n+m\log n)\),但意义不大。

【Code】

#include<algorithm>
#include<cstdio>
#include<cmath>
#define LD double
#define LL long long
#define Vector Point
#define Re register int
using namespace std;
const int N=5e4+3;
const LD eps=1e-8,Pi=acos(-1.0);
inline int dcmp(LD a){return a<-eps?-1:(a>eps?1:0);}
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
struct Point{
    LD x,y;Point(LD X=0,LD Y=0){x=X,y=Y;}
    inline void In(){Re X,Y;in(X),in(Y),x=X,y=Y;}
    inline bool operator<(const Point &O)const{return dcmp(x-O.x)?x<O.x:y<O.y;}
};
inline LD Dot(Vector a,Vector b){return a.x*b.x+a.y*b.y;}
inline LD Cro(Vector a,Vector b){return a.x*b.y-a.y*b.x;}
inline Vector operator+(Vector a,Vector b){return Vector(a.x+b.x,a.y+b.y);}
inline Vector operator-(Vector a,Vector b){return Vector(a.x-b.x,a.y-b.y);}
inline Vector operator*(Vector a,LD b){return Vector(a.x*b,a.y*b);}
inline int pan_PL(Point p,Point a,Point b){//【判断点P是否在直线AB上】
    return !dcmp(Cro(p-a,p-b))&&dcmp(Dot(p-a,p-b))<0;
}
inline bool cmp1(Vector a,Vector b){return a.x==b.x?a.y<b.y:a.x<b.x;};//按坐标排序
inline int ConvexHull(Point *P,Re n,Point *cp){//【Graham扫描法】求凸包
    sort(P+1,P+n+1,cmp1);
    Re t=0;
    for(Re i=1;i<=n;++i){//下凸包
        while(t>1&&dcmp(Cro(cp[t]-cp[t-1],P[i]-cp[t-1]))<=0)--t;
        cp[++t]=P[i];
    }
    Re St=t;
    for(Re i=n-1;i>=1;--i){//上凸包
        while(t>St&&dcmp(Cro(cp[t]-cp[t-1],P[i]-cp[t-1]))<=0)--t;
        cp[++t]=P[i];
    }
    return --t;//要减一
}
inline int PIP(Point *P,Re n,Point a){//【二分法】判断点A是否在凸多边形Poly以内
    //点按逆时针给出
    if(dcmp(Cro(a-P[1],P[2]-P[1]))>0||dcmp(Cro(P[n]-P[1],a-P[1]))>0)return 0;//在P[1_2]或P[1_n]外
    if(pan_PL(a,P[1],P[2])||pan_PL(a,P[1],P[n]))return 1;//在P[1_2]或P[1_n]上
    Re l=2,r=n-1;
    while(l<r){//二分找到一个位置pos使得P[1]_A在P[1_pos],P[1_(pos+1)]之间
        Re mid=l+r+1>>1;
        if(dcmp(Cro(a-P[1],P[mid]-P[1]))>0)r=mid-1;
        else l=mid;
    }
    return dcmp(Cro(a-P[r],P[r+1]-P[r]))<=0;
}
Vector V1[N*3],V2[N*3];
inline int Mincowski(Point *P1,Re n,Point *P2,Re m,Point *P){//【闵可夫斯基和】求两个凸包{P1},{P2}的向量集合{V}={P1+P2}构成的凸包
    for(Re i=1;i<=n;++i)V1[i]=P1[i<n?i+1:1]-P1[i];
    for(Re i=1;i<=m;++i)V2[i]=P2[i<m?i+1:1]-P2[i];
    Re t=0,i=1,j=1;P[++t]=P1[1]+P2[1];
    while(i<=n&&j<=m)++t,P[t]=P[t-1]+(dcmp(Cro(V1[i],V2[j]))>0?V1[i++]:V2[j++]);
    while(i<=n)++t,P[t]=P[t-1]+V1[i++];
    while(j<=m)++t,P[t]=P[t-1]+V2[j++];
    return t;
}
int n,T,n1,n2,n3;Point Q,P[N*3],cp[N*3],P1[N],P2[N],P3[N];
int main(){
//    freopen("123.txt","r",stdin);
    in(n1);for(Re i=1;i<=n1;++i)P1[i].In();n1=ConvexHull(P1,n1,cp);for(Re i=1;i<=n1;++i)P1[i]=cp[i];
    in(n2);for(Re i=1;i<=n2;++i)P2[i].In();n2=ConvexHull(P2,n2,cp);for(Re i=1;i<=n2;++i)P2[i]=cp[i];
    in(n3);for(Re i=1;i<=n3;++i)P3[i].In();n3=ConvexHull(P3,n3,cp);for(Re i=1;i<=n3;++i)P3[i]=cp[i];
    n=Mincowski(P1,n1,P2,n2,P),n=Mincowski(P,n,P3,n3,cp),n=ConvexHull(cp,n,P),in(T);//这里对P求一次凸包或者分开求两次凸包效果都是一样的
//    n=Mincowski(P1,n1,P2,n2,cp),n=ConvexHull(cp,n,P);
//    n=Mincowski(P,n,P3,n3,cp),n=ConvexHull(cp,n,P),in(T);
    while(T--)Q.In(),puts(PIP(P,n,Q*3.0)?"YES":"NO");
}

标签:Rea,return,题解,while,dcmp,vec,CF87E,cp,Cro
来源: https://www.cnblogs.com/Xing-Ling/p/13027432.html