【题解】Mogohu-Rea Idol [CF87E]
传送门:( ext{Mogohu-Rea Idol}) ( ext{[CF87E]})
【题目描述】
按逆时针顺序给出三个凸包点集 (A,B,C),每次查询给出点 (q),问是否存在点 (ain A,bin B,cin C) 满足 (q) 为 (Delta abc) 的重心。
【分析】
目前我所知的第三道闵可夫斯基和的题(貌似是个冷门老题)
考虑三角形重心 (Q) 的基本性质:(vec{OA}+vec{OB}+vec{OC}=3vec{OQ}) 。
然后就是个板子题了...
对三个凸包求个和,得到 (P={a+b+c|ain A,bin B,cin C}),然后直接判断点 (q*3) 是否在 (P) 以内即可。
注意题目给出的虽然是严格凸包,但还是要自己写个算法跑一下,目的是将坐标最小的放在第一个位置,否则后面对凸包求和时会出问题(也可以直接枚举找最小点作为凸包起点)。
然后就是做完闵可夫斯基和后要再对其求一下凸包,目的是去掉平行边(或者说重边。在 (P) 上表现为三点共线)。
时间复杂度:(O((n+m)log n)) 。
其实可以把两部分求凸包的过程都换掉:前面枚举找最小点、多加一个特判处理重边,就变成了 (O(n+mlog 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");
}