小学半平面交

快到省选了,赶紧学一发计算几何……
这玩意就是线性规划.
半平面通常用不等式或者两点表示,具体表示方法,就题而言,要灵活.
算法的话,什么朴素O(n^2),什么分治,都不会,只会一个弹栈的.
这个算法的话,一开始好像一定要去重,而且有的时候需要在外面加4个框,似乎是因为这个算法在相邻直线的极角跨度大于Pi的时候就不对了……似乎还是为了保证判断无解的方法(剩余直线小于3个)的正确性……
反正我就大概懂一些计算几何基础(向量啊什么的),并且感性理解了一发这个算法……
就做了几道水题:

#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=10010;
struct Race{
    double k,b;
    int id;
}race[N],stack[N];
int top;
inline bool comp(Race a,Race b){
    return a.k<b.k||(a.k==b.k&&a.b>b.b);
}
int n;
bool good[N];
inline void Init(){
    scanf("%d",&n);
    int i;
    for(i=1;i<=n;++i)
        scanf("%lf",&race[i].b);
    for(i=1;i<=n;++i)
        scanf("%lf",&race[i].k),race[i].id=i;
}
inline bool check(Race a,Race b,Race c){
    double x=(double)(b.b-a.b)/(a.k-b.k);
    double y=a.k*x+a.b;
    return y>=c.k*x+c.b;
}
inline void Work(){
    int i,j;
    std::sort(race+1,race+(n+1),comp);
    j=0;
    race[++j]=race[1];
    for(i=2;i<=n;++i)
        if(race[i].k!=race[j].k||(race[i].k==race[j].k&&race[i].b==race[j].b))
            race[++j]=race[i];
    stack[++top]=(Race){1./0.,0.,0};
    for(i=1;i<=j;++i){
        while(top>1&&!check(stack[top],stack[top-1],race[i]))--top;
        stack[++top]=race[i];
    }
}
inline void Print(){
    int i;
    for(i=1;i<=top;++i)
        good[stack[i].id]=true;
    printf("%d\n",top-1);
    for(i=1;i<=n;++i)
        if(good[i])
            printf("%d ",i);
    puts("");
}
int main(){
    Init(),Work(),Print();
    return 0;
}
bzoj3190:[JLOI2013]赛车
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=110;
const double oo=1e10;
const double eps=1e-6;
int n;
double s;
struct Peo{
    double k,b;
    inline void redef(double x,double y){
        k=1./x-1./y;
        b=s/y;
    }
    inline double query(double pos){
        return k*pos+b;
    }
}peo[N];
inline double query(double pos){
    double ret=oo;
    for(int i=1;i<n;++i)
        ret=std::min(ret,peo[i].query(pos));
    ret-=peo[n].query(pos);
    return ret;
}
int main(){
    scanf("%lf%d",&s,&n);
    int i;
    double l,r,mid1,mid2,x,y,ans1=0.,ans2=-oo;
    for(i=1;i<=n;++i){
        scanf("%lf%lf",&x,&y);
        x/=3600,y/=3600;
        peo[i].redef(x,y);
    }
    l=0.,r=s;
    while(l+eps<r){
        mid1=(r-l)/3+l;
        mid2=r-(r-l)/3;
        x=query(mid1);
        y=query(mid2);
        if(x<=y)
            ans2=y,ans1=mid2,l=mid1;
        else
            ans2=x,ans1=mid1,r=mid2;
    }
    x=query(0);
    if(x>=ans2)ans2=x,ans1=0;
    if(ans2<0)puts("NO");
    else printf("%.2f %.2f %.0f\n",ans1,s-ans1,ans2);
    return 0;
}
bzoj2765:[JLOI2010]铁人双项比赛
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=310;
const double oo=1e10;
const double eps=1e-8;
struct Poi{
    double x,y;
    inline friend double operator *(Poi a,Poi b){
        return a.x*b.y-a.y*b.x;
    }
    inline friend Poi operator -(Poi a,Poi b){
        return (Poi){a.x-b.x,a.y-b.y};
    }
}dia[N];
struct Line{
    Poi s,t;
    double k;
    inline void redef(Poi a,Poi b){
        s=a,t=b,k=std::atan2(b.y-a.y,b.x-a.x);
    }
}line[N],stack[N];
int top;
inline bool comp(Line a,Line b){
    return a.k==b.k?(b.s-a.s)*(b.t-a.s)>0:a.k<b.k;
}
int n;
inline Poi point(Line a,Line b){
    Poi ret;
    double s1=(b.s-a.s)*(b.t-a.s);
    double s2=(b.t-a.t)*(b.s-a.t);
    ret.x=(s1*a.t.x+s2*a.s.x)/(s1+s2);
    ret.y=(s1*a.t.y+s2*a.s.y)/(s1+s2);
    return ret;
}
inline bool check(Line a,Line b,Line c){
    Poi poi=point(a,b);
    return (c.s-poi)*(c.t-poi)>0;
}
inline void Init(){
    scanf("%d",&n);
    int i;
    for(i=1;i<=n;++i)
        scanf("%lf",&dia[i].x);
    for(i=1;i<=n;++i)
        scanf("%lf",&dia[i].y);
    dia[0]=(Poi){dia[1].x,oo};
    dia[n+1]=(Poi){dia[n].x,oo};
    ++n;
    for(i=1;i<=n;++i)
        line[i].redef(dia[i-1],dia[i]);
}
inline void Work(){
    std::sort(line+1,line+(n+1),comp);
    int i,j;
    j=0;
    line[++j]=line[1];
    for(i=2;i<=n;++i)
        if(line[i].k!=line[j].k)
            line[++j]=line[i];
    stack[++top]=line[1];
    stack[++top]=line[2];
    for(i=3;i<=j;++i){
        while(top>1&&!check(stack[top],stack[top-1],line[i]))--top;
        stack[++top]=line[i];
    }
}
inline double query(double pos){
    Poi p1=(Poi){pos,0},p2=(Poi){pos,oo},poi;
    Line tmp;
    tmp.redef(p1,p2);
    double ret=-oo;
    for(int i=2;i<top;++i){
        poi=point(tmp,stack[i]);
        ret=std::max(ret,poi.y);
    }
    return ret;
}
inline void Print(){
    int i,at;
    double ans=oo;
    Poi poi;
    for(i=1;i<n;++i)
        ans=std::min(ans,query(dia[i].x)-dia[i].y);
    at=1;
    for(i=3;i<top;++i){
        poi=point(stack[i-1],stack[i]);
        while(poi.x>=dia[at+1].x)++at;
        ans=std::min(ans,poi.y-((dia[at+1].y-dia[at].y)*(poi.x-dia[at].x)/(dia[at+1].x-dia[at].x)+dia[at].y));
    }
    printf("%.3f\n",ans);
}
int main(){
    //freopen("rio.txt","r",stdin);
    //freopen("wq.txt","w",stdout);
    Init(),Work(),Print();
    return 0;
}
bzoj1038:[ZJOI2008]瞭望塔
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=110;
double s=1;
int n,v[N],u[N],w[N];
struct Line{
    double a,b,c,k;
    inline void redef(double x,double y,double z){
        a=x,b=y,c=z;
        if(x<0)x=-x;
        if(x)a/=x,b/=x,c/=x;
        k=std::atan2(-a,b);
    }
}line[N],queue[N];
int head,tail;
inline bool comp(Line a,Line b){
    return a.k!=b.k?a.k<b.k:a.c<b.c;
}
inline bool check(Line a,Line b,Line c){
    double x=(b.c*a.b-a.c*b.b)/(a.a*b.b-a.b*b.a);
    double y=(b.c*a.a-a.c*b.a)/(a.b*b.a-a.a*b.b);
    return c.a*x+c.b*y+c.c>0;
}
inline void Init(){
    scanf("%d",&n);
    int i;
    for(i=1;i<=n;++i)
        scanf("%d%d%d",&v[i],&u[i],&w[i]);
}
inline bool check(int id){
    int m=5,i,j;
    line[1].redef(1,0,0);
    line[2].redef(-1,0,s);
    line[3].redef(0,1,0);
    line[4].redef(0,-1,s);
    line[5].redef(-1,-1,s);
    double a,b,c;
    for(i=1;i<=n;++i){
        if(i==id)continue;
        if(v[id]<=v[i]&&u[id]<=u[i]&&w[id]<=w[i])return false;
        a=1./v[id]-1./v[i];
        b=1./u[id]-1./u[i];
        c=1./w[id]-1./w[i];
        ++m;
        line[m].redef(c-a,c-b,-c*s);
    }
    std::sort(line+1,line+(m+1),comp);
    j=0;
    line[++j]=line[1];
    for(i=2;i<=m;++i)
        if(line[i].k!=line[j].k)
            line[++j]=line[i];
    head=tail=0;
    queue[tail++]=line[1];
    queue[tail++]=line[2];
    for(i=3;i<=j;++i){
        while(tail-head>1&&!check(queue[tail-1],queue[tail-2],line[i]))--tail;
        while(tail-head>1&&!check(queue[head],queue[head+1],line[i]))++head;
        queue[tail++]=line[i];
    }
    while(tail-head>1&&!check(queue[tail-1],queue[tail-2],queue[head]))--tail;
    return tail-head>2;
}
inline void Print(){
    int i;
    for(i=1;i<=n;++i)
        puts(check(i)?"Yes":"No");
}
int main(){
    Init();Print();
    return 0;
}
bzoj3800:Saber VS Lancer
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=100010;
const double oo=1e20;
int n,cnt;
struct Line{
    double a,b,c,k;
    int id;
    inline void redef(double x,double y,double z,int name){
    //如果x、y都为0,那么若z>=0成立,则整张图都成立,若不成立,则整张图都不成立
        id=name;
        a=x,b=y,c=z;
        if(x<0)x=-x;
        if(x)a/=x,b/=x,c/=x;
        else{
            if(y<0)y=-y;
            if(y)a/=y,b/=y,c/=y;
        }
        k=std::atan2(-a,b);
    //讨论一下4种平行、外加Delta的计算,就会发现这是对的 
    }
}keep[N<<1],line[N<<1],queue[N<<1];
int head,tail;
inline bool comp(Line a,Line b){
    return a.k!=b.k?a.k<b.k:a.c<b.c;
    //在我们上面进行了除x且除y的操作之后,这样是对的 
}
inline bool check(Line a,Line b,Line c){
    double x=(a.b*b.c-b.b*a.c)/(b.b*a.a-a.b*b.a);
    double y=(a.a*b.c-b.a*a.c)/(b.a*a.b-a.a*b.b);
    return c.a*x+c.b*y+c.c>=0;
    //手推一下就能出来,只要记住x的主料为b、y的主料为a、上下主料相反、单项相反、上c下反即可 
} 
inline bool check(int k){
    int i,j=0;
    for(i=1;i<=cnt;++i)
        if(keep[i].id<=k&&(j==0||keep[i].k!=line[j].k))
            line[++j]=keep[i];
    head=tail=0;
    queue[tail++]=line[1];
    queue[tail++]=line[2];
    for(i=3;i<=j;++i){
        while(tail-head>1&&!check(queue[tail-1],queue[tail-2],line[i]))--tail;
        while(tail-head>1&&!check(queue[head],queue[head+1],line[i]))++head;
        queue[tail++]=line[i];
    }
    while(tail-head>1&&!check(queue[tail-1],queue[tail-2],queue[head]))--tail;
    return tail-head>=3;
}
inline void Init(){
    cnt=4;
    keep[1].redef(1,0,oo,0);
    keep[2].redef(-1,0,0,0);
    keep[3].redef(0,1,oo,0);
    keep[4].redef(0,-1,oo,0);
    scanf("%d",&n);
    int i,j,x,y,z;
    for(i=1;i<=n;++i){
        scanf("%d%d%d",&x,&y,&z);
        keep[++cnt].redef((double)x*x,x,-y,i);
        keep[++cnt].redef(-(double)x*x,-x,z,i);
    }
    std::sort(keep+1,keep+(cnt+1),comp);
}
inline void Work(){
    int l=1,r=n,mid,ans=0;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))
            ans=mid,l=mid+1;
        else
            r=mid-1;
    }
    printf("%d\n",ans);
}
int main(){
    Init(),Work();
    return 0;
}
bzoj2732:[HNOI2012]射箭
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define pf(a) ((double)(a)*(a))
const int N=610;
int n;
struct V{
    int to,next;
}c[N*N*2];
int first[N],t;
int di[N],qu[N],front,back;
int aks[N],wai[N];
inline void add(int x,int y){
    c[++t].to=y,c[t].next=first[x],first[x]=t;
}
struct Line{
    double a,b,c,k;
    int id;
    inline void redef(double x,double y,double z,int name){
        a=x,b=y,c=z,id=name;
        if(x<0)x=-x;
        if(x)a/=x,b/=x,c/=x;
        else{
            if(y<0)y=-y;
            if(y)a/=y,b/=y,c/=y;
        }
        k=std::atan2(-a,b);
    }
}line[N],queue[N];
int head,tail;
inline bool comp(Line a,Line b){
    return a.k!=b.k?a.k<b.k:a.c<b.c;
}
inline bool check(Line a,Line b,Line c){
    double x=(a.b*b.c-b.b*a.c)/(b.b*a.a-a.b*b.a);
    double y=(a.a*b.c-b.a*a.c)/(b.a*a.b-a.a*b.b);
    return c.a*x+c.b*y+c.c>0;
}
inline void build(int id,int li,int im){
    int i,j=1,m=4;
    line[1].redef(1,0,0,0);
    line[2].redef(-1,0,li,0);
    line[3].redef(0,1,0,0);
    line[4].redef(0,-1,im,0);
    double a,b,c;
    for(i=1;i<=n;++i){
        if(i==id)continue;
        a=2*aks[id]-2*aks[i];
        b=2*wai[id]-2*wai[i];
        c=pf(aks[i])-pf(aks[id])+pf(wai[i])-pf(wai[id]);
        line[++m].redef(a,b,c,i);
    }
    std::sort(line+1,line+(m+1),comp);
    for(i=2;i<=m;++i)
        if(line[i].k!=line[j].k)
            line[++j]=line[i];
    head=tail=0;
    queue[tail++]=line[1];
    queue[tail++]=line[2];
    for(i=3;i<=j;++i){
        while(tail-head>1&&!check(queue[tail-1],queue[tail-2],line[i]))--tail;
        while(tail-head>1&&!check(queue[head],queue[head+1],line[i]))++head;
        queue[tail++]=line[i];
    }
    while(tail-head>1&&!check(queue[tail-1],queue[tail-2],queue[head]))--tail;
    for(i=head;i<tail;++i)
        add(id,queue[i].id);
}
inline int bfs(int s){
    memset(di,-1,sizeof(di));
    front=back=0;
    qu[back++]=s;
    di[s]=0;
    int x,i;
    while(front!=back){
        x=qu[front++];
        for(i=first[x];i;i=c[i].next)
            if(di[c[i].to]==-1){
                if(!c[i].to)return di[x]+1;
                di[c[i].to]=di[x]+1;
                qu[back++]=c[i].to;
            }
    }
}
inline void Main(){
    memset(first,0,sizeof(first)),t=0;
    scanf("%d",&n);
    int i,w,q,l,m,id;
    double min,dis;
    scanf("%d%d%d%d",&l,&m,&w,&q);
    if(n==0){
        puts("0");
        return;
    }
    for(i=1;i<=n;++i){
        scanf("%d%d",&aks[i],&wai[i]);
        dis=pf(w-aks[i])+pf(q-wai[i]);
        if(i==1||dis<min)
            id=i,min=dis;
    }
    for(i=1;i<=n;++i)
        build(i,l,m);
    printf("%d\n",bfs(id));
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--)
        Main();
    return 0;
}
bzoj3199:[Sdoi2013]escape

一般姿势正确就不会被卡精,但是被卡精卡到怀疑人生也是常有的事儿……做题的时候感觉二分/三分和半平面交有互相替代的关系……

posted @ 2018-04-10 19:29  TS_Hugh  阅读(...)  评论(... 编辑 收藏