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


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

    传送门:折纸 ( ext{origami [SCOI2007] [P4468]}) ( ext{[Bzoj1074]})

    【题目描述】

    初始有一个 (100*100) 的正方形纸片,现给出 (n) ((0 leqslant n leqslant 8)) 条直线,将该纸片依次按照直线进行折叠,结束后会给出 (m) 个询问,每次询问求某个坐标上的点穿过了几层纸。

    【分析】

    【计算几何全家桶】

    又是一道毒瘤膜您题。

    由于数据范围较小,可以直接上暴力。

    考虑记录当前已有的多边形(初始为一个 (100*100) 的正方形),每折叠一次就把折叠线所穿过的所有多边形分成左右两部分,然后将右边部分的点全部关于折叠线对称,得到两个新的小多边形。

    对于每次询问,暴力枚举统计 包含询问点的多边形 即可。

    折叠 (n) 次后最多会出现 (2^n) 个多边形,每个多边形最多有 (n^2) 个点,射线法做一次 (PIP)(O(n^2)),总时间复杂度为: (O(m n^2 2^n))

    注意:如果用的是 (double) 而不是 (long double)(eps) 开得过于小会导致答案出锅。

    【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
    ",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
    ",ans);
        }
    }
    
  • 相关阅读:
    c/c++(c++和网络编程)日常积累(二)
    docker日常积累
    c/c++日常积累
    qt日常积累
    YUV相关积累
    02-asio学习
    01--c实现基础客户端和服务端与c++ boost.asio实现对比
    webrtc学习笔记积累
    linux-日常工作积累
    用Java链接SQL Server
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12120274.html
Copyright © 2020-2023  润新知