Luogu3297 SDOI2013逃考(半平面交+最短路)

Posted gloid

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu3297 SDOI2013逃考(半平面交+最短路)相关的知识,希望对你有一定的参考价值。

  把每个人的监视范围看成点,相邻的两个监视范围连边,那么跑一遍最短路就可以了(事实上边权都为1可以直接bfs)。显然最优的话不会有某个时刻同时被多人监视,要跨过去的话完全可以经过分界线而不是交点。

  现在问题是怎么求出哪些监视范围相邻。考虑对于某个人的监视范围求出所有与它相邻的。两个监视范围的公共边是这两个人连线的中垂线,把这些线画出来可以发现求个半平面交就好了。注意线要求在矩形范围内。如果直线在半平面交中只剩下一个点应该去掉。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<0||c>9) {if (c==-) f=-1;c=getchar();}
    while (c>=0&&c<=9) x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 610
#define vector dot
int T,n,p[N],d[N],queue[N],cnt;
bool flag[N];
const double eps=1E-10;
struct data{int to,nxt;
}edge[N*N];
struct dot
{
    double x,y;
    vector operator +(const vector&a) const
    {
        return (vector){x+a.x,y+a.y};
    }
    vector operator -(const vector&a) const
    {
        return (vector){x-a.x,y-a.y};
    }
    double operator *(const vector&a) const
    {
        return x*a.y-y*a.x;
    }
    vector operator *(const double a) const
    {
        return (vector){a*x,a*y};
    }
    double len()
    {
        return sqrt(x*x+y*y);
    }
    vector rotate()
    {
        return (vector){-y,x};
    }
}a[N],P[N];
struct line
{
    dot a;vector p;int i;
    bool operator <(const line&a) const
    {
        return atan2(p.x,p.y)>atan2(a.p.x,a.p.y);
    }
}q[N],Q[N];
void addedge(int x,int y){cnt++;edge[cnt].to=y,edge[cnt].nxt=p[x],p[x]=cnt;}
bool onright(line x,dot y)
{
    return (y-x.a)*x.p>=0;
}
dot cross(line x,line y)
{
    return y.a+y.p*(x.p*(x.a-y.a)/(x.p*y.p));
}
int bfs(int S)
{
    memset(d,42,sizeof(d));
    int head=0,tail=1;queue[1]=S;d[S]=0;
    do
    {
        int x=queue[++head];
        for (int i=p[x];i;i=edge[i].nxt)
        if (d[x]+1<d[edge[i].to])
        {
            d[edge[i].to]=d[x]+1;
            queue[++tail]=edge[i].to;
            if (!edge[i].to) return d[edge[i].to];
        }
    }while (head<tail);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("3297.in","r",stdin);
    freopen("3297.out","w",stdout);
    const char LL[]="%I64d";
#else
    const char LL[]="%lld";
#endif
    T=read();
    while (T--)
    {
        n=read();
        int r=read(),c=read();
        dot s;s.x=read(),s.y=read();
        for (int i=1;i<=n;i++)
        a[i].x=read(),a[i].y=read();
        int S;
        double dis=(a[1]-s).len();
        for (int i=2;i<=n;i++) dis=min(dis,(a[i]-s).len());
        for (int i=1;i<=n;i++) if (fabs(dis-(a[i]-s).len())<eps) S=i;
        cnt=0;
        memset(p,0,sizeof(p));
        for (int j=1;j<=n;j++)
        {
            int t=0;
            for (int i=1;i<=n;i++)
            if (i!=j) q[++t]=(line){(a[i]+a[j])*0.5,(a[i]-a[j]).rotate(),i};
            q[++t]=(line){(dot){0,0},(vector){1,0},0};
            q[++t]=(line){(dot){r,0},(vector){0,1},0};
            q[++t]=(line){(dot){r,c},(vector){-1,0},0};
            q[++t]=(line){(dot){0,c},(vector){0,-1},0};
            sort(q+1,q+t+1);
            int head=1,tail=1;Q[1]=q[1];
            for (int i=2;i<=t;i++)
            {
                while (head<tail&&onright(q[i],P[tail])) tail--;
                while (head<tail&&onright(q[i],P[head+1])) head++;
                Q[++tail]=q[i];
                if (fabs(Q[tail-1].p*Q[tail].p)<eps)
                {
                    tail--;
                    if (onright(q[i],Q[tail].a)) Q[tail]=q[i];
                }
                if (head<tail) P[tail]=cross(Q[tail],Q[tail-1]);
            }
            while (head<tail&&onright(Q[head],P[tail])) tail--;
            P[head]=cross(Q[head],Q[tail]);
            for (int i=head;i<=tail;i++) addedge(j,Q[i].i);
        }
        printf("%d
",bfs(S));
    }
    return 0;
}

 

以上是关于Luogu3297 SDOI2013逃考(半平面交+最短路)的主要内容,如果未能解决你的问题,请参考以下文章

P3297 [SDOI2013]逃考

bzoj3199 [Sdoi2013]escape

BZOJ 3199: [Sdoi2013]escape

BZOJ 3190 3190: [JLOI2013]赛车 (半平面交)

半平面交

半平面交