洛谷P3297 [SDOI2013] 逃考 题解

洛谷P3297 [SDOI2013] 逃考 题解

题目链接:P3297 [SDOI2013] 逃考

题意:髙考又来了,对于不认真读书的小杨来讲,真不是个好消息。为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外公外婆、大舅、大嫂、阿姨…

小杨实在是忍无可忍了,这种生活跟监狱有什么区别!为了他亲爱的小红,为了他的dota,他决定越狱!

假设小杨的家是个 x 1 × y 1 x_1\times y_1 x1×y1 的矩阵,左下角坐标为 ( 0 , 0 ) (0,0) (0,0) ,右上角坐标为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 。小杨有 n n n 个亲戚,驻扎在矩阵里(位置不同,且不在矩阵的边上)。小杨家里的每个地方都被亲戚监控着,而且只被距离最近的亲戚监控:

也就是说假设小杨所在的位置是 ( 3 , 3 ) (3,3) (3,3) ,亲戚A在 ( 3 , 0 ) (3,0) (3,0) , A距离小杨距离是 3 3 3;亲戚B在 ( 6 , 7 ) (6,7) (6,7) ,则B距离小杨距离是 5 5 5 。距离A<距离B,所以 ( 3 , 3 ) (3,3) (3,3) 位置由A监控。

如果“最近距离”出现同时有几个亲戚,那么那个位置同时被那几个亲戚监控。

给出小杨的坐标 ( x 0 , y 0 ) (x_0,y_0) (x0,y0) 。因为被发现的人数越少,越狱成功的机会越大,所以小杨需要你设计一条越狱路线到达矩形的边上,且被发现的人数最少。

ps:小杨走的方向是任意的,也就是说路线上的任意位置需要是实数。

保证一开始小杨只被一个亲戚监控着。

注:这题目题面和数据有点问题,要注意特判掉那些在矩形外面的点

鉴于数据可能改掉(那样我画的图就没用了),给出原来样例的第二个测试数据:

1
17
14 12 7 6
7 11
6 9
7 7
1 10
2 20
1 6
2 6
1 1
2 2
5 1
5 2
13 1
12 2
12 7
13 7
12 11
13 11

本题解法:半平面交+最短路

注意到每个亲戚都有一个管辖的范围

因此我们可以把每个亲戚向与其管辖范围相邻的亲戚连一条边权为 1 1 1 的边

然后从第一个监管小杨的亲戚那里跑一下最短路就好了

那么管辖范围怎么求呢?

注意到对于亲戚 u , v u,v u,v ,当小杨从 u u u 的管辖范围转移到 v v v 的范围时

一定经过了一条分界线

这条分界线就是直线 u − v u-v uv 的中垂线(垂直平分线)

这样,每个亲戚的管辖范围就是他与所有其他亲戚的中垂线的半平面交

对于上面那个样例,我画了个图,(没画不合法的点)

看在q779花了1个小时的分上,点个赞吧

在这里插入图片描述

其实这个图形就是个泰森多边形(知道也没用

那么这个题好像就没啥难度了啊

时间复杂度 O ( Q n 2 log ⁡ n ) O(Qn^2\log n) O(Qn2logn)

代码如下

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
#define lb lower_bound
#define N (int)(666)
int n,s,t;
double w,h;
// ---------------vct--------------------
const double eps=1e-15;
#define pf(x) ((x)*(x))
struct vct{double x,y;}lamb,p[N],in[N];
vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}
vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}
vct operator*(vct a,double b){return {a.x*b,a.y*b};}
vct operator/(vct a,double b){return {a.x/b,a.y/b};}
double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}
double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}
double len(vct a){return sqrt(pf(a.x)+pf(a.y));}
double dis(vct a,vct b){return len(a-b);}
vct rot(vct a){return {-a.y,a.x};}
int dcmp(double x){if(fabs(x)<=eps)return 0;return x>eps?1:-1;}
// ---------------line--------------------
struct line
{
    vct p,way;
    double k;
    int id;
    void mkline(vct a,vct b,int ID)
    {
        p=a;way=b;
        k=atan2(b.y,b.x);
        id=ID;
    }
    void mk(double x1,double y1,double x2,double y2,int ID)
        {mkline({x1,y1},{x2-x1,y2-y1},ID);}
}a[N];
vct intersect(line a,line b)
{
    double t=cross(b.way,a.p-b.p)/cross(a.way,b.way);
    return a.p+a.way*t;
}
line getmidline(vct a,vct b,int id)
{
    line t;
    t.mkline((a+b)/2,rot(b-a),id);
    return t;
}
// ---------------SSSP--------------------
struct dist{int u,dis;};
bool operator<(dist a,dist b){return a.dis>b.dis;}
struct Edge{int u,v,w,next;}e[(int)(8e5+15)];
priority_queue<dist> q;
int d[N],vis[N];
int head[N],pos=1;
void addEdge(int u,int v,int w)
{
    e[++pos]={u,v,w,head[u]};
    head[u]=pos;
}
int dijkstra()
{
    memset(vis,0,(n+15)*sizeof(int));
    memset(d,0x3f,(n+15)*sizeof(int));
    while(!q.empty())q.pop();
    d[s]=0;q.push({s,0});
    while(!q.empty())
    {
        int u=q.top().u;q.pop();
        if(vis[u])continue;
        vis[u]=1;
        for(int i=head[u]; i; i=e[i].next)
        {
            int v=e[i].v,w=e[i].w;
            if(d[v]>d[u]+w)
            {
                d[v]=d[u]+w;
                q.push({v,d[v]});
            }
        }
    }
    // for(int i=0; i<=n+5; i++)
    //     cout << (d[i]==INF?-1:d[i]) << " \n"[i==n+5];
    return d[t];
}
int init(int k)
{
    int o=0;
    for(int i=1; i<=n; i++)
    {
        if(i==k)continue;
        a[++o]=getmidline(in[k],in[i],i);
    }
    // o=n-1
    a[++o].mk(-eps,-eps,w,-eps,0);
    a[++o].mk(w,-eps,w,h,0);
    a[++o].mk(w,h,-eps,h,0);
    a[++o].mk(-eps,h,-eps,-eps,0);
    // o=n-1+4=n+3
    return o;
}
// ---------------halfplane--------------------
bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}
bool operator<(line a,line b)
{
    int d=dcmp(a.k-b.k);
    if(d==0)return dcmp(cross(b.p-a.p,b.way))>0;
    return d<0;
}
int st,en;
line que[N];
int qc[N];
void proc(int k)
{
    int oo=0,o=0;
    for(int i=st; i<=en; i++)
        qc[++oo]=que[i].id;
    sort(qc+1,qc+1+oo);
    o=unique(qc+1,qc+1+oo)-qc-1;
    for(int i=1; i<=o; i++)
        addEdge(k,qc[i],1),
        addEdge(qc[i],k,1);
}
void halfplane(int k,int lcnt)
{
    int o=0;
    // cout << lcnt << endl;
    sort(a+1,a+1+lcnt);
    // cout << "q779 is very cute." << endl;
    for(int i=1; i<=lcnt; i++)
        if(i==1||dcmp(a[i].k-a[i-1].k))
            a[++o]=a[i];
    que[st=en=1]=a[1];
    for(int i=2; i<=o; i++)
    {
        while(st<en&&onright(p[en],a[i]))--en;
        while(st<en&&onright(p[st+1],a[i]))++st;
        que[++en]=a[i];
        if(st<en)p[en]=intersect(que[en-1],que[en]);
    }
    while(st<en&&onright(p[en],que[st]))--en;
    if(en-st<=1)return;
    p[st]=intersect(que[st],que[en]);
    proc(k);
    // cout << pos << endl;
}
void clear()
{
    pos=1;t=0;
    memset(head,0,sizeof(head));
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int Q;
    cin >> Q;
    while(Q--)
    {
        clear();
        cin >> n;
        if(!n){cout << 0 << endl;continue;}
        cin >> w >> h >> lamb.x >> lamb.y;
        double len=INF;
        int tmp=0,o=0;
        for(int i=1; i<=n; i++)
        {
            double x,y;
            cin >> x >> y;
            if(x>w||x<0||y>h||y<0)continue;
            in[++o]={x,y};
            double now=dis(in[i],lamb);
            if(dcmp(len-now)>0)len=now,tmp=o;
        }
        n=o;s=tmp;
        // cout << o << endl;
        for(int i=1; i<=n; i++)
        {
            int lcnt=init(i);
            // cout << lcnt << endl;
            halfplane(i,lcnt);
            // cout << "i=" << i << ",";
            // for(int j=head[i]; j; j=e[j].next)
            //     cout << e[j].v << " ";
            // cout << endl;
        }
        cout << dijkstra() << endl;
    }
    return 0;
}

转载请说明出处

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值