# 【题解】折纸 origami [SCOI2007] [P4468] [Bzoj1074]

【计算几何全家桶】

## 【Code】

#include<algorithm>
#include<cstdio>
#include<cmath>
#define LD double
#define LL long long
#define Re register int
#define Vector Point
using namespace std;
const int N=8;
const LD eps=1e-8;
inline int dcmp(LD a){return a<-eps?-1:(a>eps?1:0);}//处理精度
inline LD Abs(LD a){return a*dcmp(a);}//取绝对值
struct Point{
LD x,y;Point(LD X=0,LD Y=0){x=X,y=Y;}
inline void in(){scanf("%lf%lf",&x,&y);}
inline void out(){printf("%.2lf %.2lf\n",x,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 LD Len(Vector a){return sqrt(Dot(a,a));}//【模长】
inline LD Angle(Vector a,Vector b){return acos(Dot(a,b)/Len(a)/Len(b));}//【两向量夹角】
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 bool operator==(Point a,Point b){return !dcmp(a.x-b.x)&&!dcmp(a.y-b.y);}//两点坐标重合则相等
inline int pan_PL(Point p,Point a,Point b){//【判断点P是否在线段AB上】
return !dcmp(Cro(p-a,b-a))&&dcmp(min(a.x,b.x)-p.x)<=0&&dcmp(p.x-max(a.x,b.x))<=0&&dcmp(min(a.y,b.y)-p.y)<=0&&dcmp(p.y-max(a.y,b.y))<=0;
//PA,AB共线且P在AB之间
}
inline int pan_PL_(Point p,Point a,Point b){//【判断点P是否在直线AB上】
return !dcmp(Cro(p-a,b-a));//PA,AB共线
}
inline Point FootPoint(Point p,Point a,Point b){//【点P到直线AB的垂足】
Vector x=p-a,y=p-b,z=b-a;
LD len1=Dot(x,z)/Len(z),len2=-1.0*Dot(y,z)/Len(z);//分别计算AP,BP在AB,BA上的投影
return a+z*(len1/(len1+len2));//点A加上向量AF
}
inline Point Symmetry_PL(Point p,Point a,Point b){//【点P关于直线AB的对称点】
return p+(FootPoint(p,a,b)-p)*2;//将PF延长一倍即可
}
inline Point cross_LL(Point a,Point b,Point c,Point d){//【两直线AB,CD的交点】
Vector x=b-a,y=d-c,z=a-c;
return a+x*(Cro(y,z)/Cro(x,y));//点A加上向量AF
}
inline int pan_cross_L_L(Point a,Point b,Point c,Point d){//【判断直线AB与线段CD是否相交】
return pan_PL(cross_LL(a,b,c,d),c,d);//直线AB与直线CD的交点在线段CD上
}
inline int PIP(Point *P,Re n,Point a){//【射线法】判断点A是否在任意多边形Poly以内
Re cnt=0;LD tmp;
for(Re i=1;i<=n;++i){
Re j=i<n?i+1:1;
if(pan_PL(a,P[i],P[j]))return 2;//点在多边形上
if(a.y>=min(P[i].y,P[j].y)&&a.y<max(P[i].y,P[j].y))//纵坐标在该线段两端点之间
tmp=P[i].x+(a.y-P[i].y)/(P[j].y-P[i].y)*(P[j].x-P[i].x),cnt+=dcmp(tmp-a.x)>0;//交点在A右方
}
return cnt&1;//穿过奇数次则在多边形以内
}
inline int judge(Point a,Point L,Point R){//判断AL是否在AR右边
return dcmp(Cro(L-a,R-a))>0;
}
struct Poly{int n;Point P[N*N+3];}Py[(1<<N)+3],Qy[(1<<N)+3];
int n,t,tt,T;Point a,b;
inline void sakura(Poly Po,Point a,Point b){
Poly L,R;L.n=R.n=0;
for(Re i=1;i<=Po.n;++i){
if(judge(a,Po.P[i],b))R.P[++R.n]=Symmetry_PL(Po.P[i],a,b);//点Po.P[i]在直线ab右边
else if(pan_PL_(Po.P[i],a,b))L.P[++L.n]=R.P[++R.n]=Po.P[i];//点Po.P[i]在直线ab上
else L.P[++L.n]=Po.P[i];//点Po.P[i]在直线ab左边
Re j=i<Po.n?i+1:1;
if(pan_cross_L_L(a,b,Po.P[i],Po.P[j]))L.P[++L.n]=R.P[++R.n]=cross_LL(a,b,Po.P[i],Po.P[j]);//如果直线AB与线段P[i]-P[i+1]有交点，将这个交点入队
while(L.n>1&&L.P[L.n]==L.P[L.n-1])--L.n;//可能会重复如归，这里迅速把它去掉
while(R.n>1&&R.P[R.n]==R.P[R.n-1])--R.n;//同上
}
if(L.n>1&&L.P[1]==L.P[L.n])--L.n;//注意最后判断首尾两点是否重合
if(R.n>1&&R.P[1]==R.P[R.n])--R.n;//同上
if(L.n)Qy[++tt]=L;//如果小矩形不为空就记录下来
if(R.n)Qy[++tt]=R;
}
int main(){
//  freopen("123.txt","r",stdin);
scanf("%d",&n);
Py[++t].n=4,Py[t].P[1]=Point(0,0),Py[t].P[2]=Point(0,100),Py[t].P[3]=Point(100,100),Py[t].P[4]=Point(100,0);
//初始化为一个正方形
while(n--){
a.in(),b.in(),tt=0;
for(Re i=1;i<=t;++i)sakura(Py[i],a,b);//切割目前已有的多边形
t=tt;
for(Re i=1;i<=tt;++i)Py[i]=Qy[i];
}
scanf("%d",&T);
while(T--){
a.in();Re ans=0;
for(Re i=1;i<=t;++i)if(PIP(Py[i].P,Py[i].n,a)==1)++ans;//严格在多边形内才统计答案
printf("%d\n",ans);
}
}

posted @ 2019-12-30 16:12  辰星凌  阅读(74)  评论(0编辑  收藏