BZOJ4814[Cqoi2017]小Q的草稿

来源:互联网 发布:linux lvs 配置 编辑:程序博客网 时间:2024/06/05 13:36

题目

BZOJ4814[Cqoi2017]小Q的草稿

题解

BZOJ这题通过率好像特低。。不造为啥。可能是数据不好造+容易写错。

我也是写了大半天迷迷糊糊的,找了篇题解思路才清晰起来。

首先要注意到这么一点,因为三角形不相交,那如果把三角形拆成三条线段,则不经过三角形区域即为关键点连线不与任何一条线段相交。

看到这题就应该很自然的联想到,枚举一个点然后极角排序然后维护一个啥。
很快我们发现,线段间的关系是不变的,大概意思就是,每条线段会遮盖一个极角区域,如果线段a在某个区域盖住了线段b(极角区域),线段b就不可能在某个区域盖住线段a。除非a消失,否则b一直被盖着。

画画图很好看出来的qw。不过这个实现特别蛋疼。

因为我写的计算几何题特别少。写了好久好久。最后还是懵逼。

我们需要发现这个事情,每个三角形只有一条边是有用的。起初我以为是一条或者两条,后来我发现。。点不可能在三角形内部,所以本质上还是只要一条,就是极角夹角(我不造是不是该这么叫。。可能看代码更好懂)最大的那两个点构成的那条边。

然后问题就简化了。只有线段和点。就打加线段标记,删除线段标记,然后那一堆按极角排序,用个set维护一下线段间的关系就好了。

一个小技巧就是,之前我一直被跨越x负半轴的线段所困扰,因为那两个极角、、、很蛋疼。但是某题解的作者用了一种非常棒的方法qwq。就是找出跟x轴负半轴的交点,然后拆成两条线段。这个方法真的解决了无数的困扰!!!!!!!在此特别向像我一样的计算几何萌新一定要记住这个方法!!!!因为枚举+扫描线+数据结构的题太多辣,这个思路也太妙啦。(dalao求不嘲讽萌新qwq)

个人觉得这道题考代码实现能力。比如说set里的小于号的定义要开一个全局变量。你得明白哪些set的操作要用小于号,否则会GG。

啊非常感谢某题解作者qwq,代码写的非常棒。
http://blog.csdn.net/u013849646/article/details/70197393

代码

//QWsin#include<set>#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn=2000+10;const double eps=1e-9;const double PI=acos(-1.0);inline int dcmp(const double &x){    return x<-eps?-1:(x>eps ? 1:0);}struct point{    double x,y,ang;    point(){x=y=0;}     point (double x,double y):x(x),y(y){}    inline void getang(){ang=atan2(y,x);}    inline void input(){scanf("%lf%lf",&x,&y);}    inline bool operator < (const point &rhs)const{        return dcmp(ang-rhs.ang)<0;    }}p[maxn],pp[maxn],tr[maxn][5],tt[maxn][5];typedef point vc;inline vc operator - (const point &a,const point &b){return vc(a.x-b.x,a.y-b.y);}inline vc operator + (const point &a,const vc &b){return vc(a.x+b.x,a.y+b.y);}inline vc operator * (const vc &v,const double &k){return vc(v.x*k,v.y*k);}inline vc operator / (const vc &v,const double &k){return vc(v.x/k,v.y/k);}inline double dot(const vc &a,const vc &b){return a.x*b.x+a.y*b.y;}inline double cross(const vc &a,const vc &b){return a.x*b.y-a.y*b.x;}inline double getl(const vc &v){return (v.x*v.x+v.y*v.y);}inline double Ang(const point &a,const point &b){return (dot(a,b)/sqrt(getl(a))/sqrt(getl(b)));}inline vc jiao(const point &p1,const vc &v1,const point &p2,const vc &v2){    vc u=p1-p2;    double t=cross(v2,u)/cross(v1,v2);    return p1+v1*t; }int n,m;inline void init_data(){    cin>>n>>m;    for(int i=1;i<=n;++i) p[i].input();    for(int i=1;i<=m;++i)        for(int j=0;j<3;++j)            tr[i][j].input();}const point O=point(0,0);vc base;//扫描线当前的位置 struct Line{    point x,y;    double ang;    int k,id;    Line(){x=y=point();ang=0;k=0;}    Line(point x,point y,double ang,int k,int id):x(x),y(y),ang(ang),k(k),id(id){}    inline bool operator < (const Line &rhs)const{        double l1=getl(jiao(O,base,x,y-x));        double l2=getl(jiao(O,base,rhs.x,rhs.y-rhs.x));        return l1<l2;    }}L[maxn*2];set<Line>st;set<Line>::iterator it,iit[maxn];inline int cmp(const Line &a,const Line &b){    return dcmp(a.ang-b.ang) < 0 || (dcmp(a.ang-b.ang)==0&&a.k > b.k);  }int tot;inline void prework(int chose){    point P=p[chose];    for(int i=1,_=0;i<=n;++i) if(i!=chose) {        pp[++_]=p[i]-P;pp[_].getang();    }    sort(pp+1,pp+n);    tot=0;int id_cnt=0;    for(int i=1;i<=m;++i)    {        double mx=1e6;        for(int j=0;j<3;++j) tt[i][j]=tr[i][j]-P;        tt[i][3]=tt[i][0];        point u,v;        for(int j=0;j<3;++j)        {            //因为点不会在三角形内部所以只有一条边有用(横跨最大的那条边)            double ang=Ang(tt[i][j],tt[i][j+1]);//这里Ang没用acos,因为这个角度肯定小于180,减少acos调用次数             if(ang < mx){mx=ang;u=tt[i][j],v=tt[i][j+1];}        }        u.getang();v.getang();        if(u.ang > v.ang) swap(u,v);        if(v.ang - u.ang < PI) {            ++id_cnt;            L[++tot]=Line(u,v,u.ang,1,id_cnt);            L[++tot]=Line(u,v,v.ang,0,id_cnt);        }        else//过交界拆成两条线段,一个非常重要的技巧!!!         {            point tmp=jiao(u,v-u,O,point(-1,0));            tmp.ang=dcmp(u.ang)==1?PI:-PI;            ++id_cnt;            L[++tot]=Line(tmp,u,tmp.ang,1,id_cnt);            L[++tot]=Line(u,tmp,u.ang,0,id_cnt);            tmp.ang=dcmp(v.ang)==1?PI:-PI;            ++id_cnt;            L[++tot]=Line(v,tmp,v.ang,1,id_cnt);            L[++tot]=Line(tmp,v,tmp.ang,0,id_cnt);        }    }    sort(L+1,L+tot+1,cmp);}inline int solve(int id){    base=vc(0,0);    if(id!=1)st.clear();    int j=1,ans=0;    for(int i=1;i<n;++i)    {        while(j<=tot && (dcmp(L[j].ang-pp[i].ang) < 0 ||(dcmp(L[j].ang-pp[i].ang)==0&&L[j].k))){            base=L[j].x;//earse的时候也会用到base!!                if(L[j].k==0)                 st.erase(iit[L[j].id]);            else{                iit[L[j].id]=st.insert(L[j]).first;            }            ++j;        }        if(st.size()==0) {++ans;continue;}        base=p[i];        it=st.begin();        double l1=getl(pp[i]);        double l2=getl(jiao(O,pp[i],it->x,it->y - it->x));        ans+=(dcmp(l1 - l2)<0);    }//  printf("%d %d\n",id,st.size());    return ans;}int main(){    init_data();    int ans=0;    for(int i=1;i<=n;++i)    {        prework(i);        ans+=solve(i);    }    cout<<ans/2;    return 0;}
0 0
原创粉丝点击