P3297 [SDOI2013]逃考

题目描述

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

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

假设小杨的家是个n*m的矩阵,左下角坐标为(0, 0),右上角坐标为(xl, yl)。小 杨有n个亲戚,驻扎在矩阵里(位置不同,且不在矩阵的边上)。小杨家里的每个地方都被 亲戚监控着,而且只被距离最近的亲戚监控:

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

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

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

Ps:小杨做的方向是任意的,也就是说路线上的任意位置H需耍是实数。

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

输入格式

第一行 一个正整数t<=3表示数据个数

接下来t个数据:

第一行n表示亲戚个数

第二行4个正整数表示举行右上角坐标(x1,y1)和小杨的坐标(x0,y0)

接下来n行,每行2个正整数表示一个亲戚的位置

输出格式

每个数据一个正整数表示越狱被发现人数的最小值

输入输出样例

输入 #1

2
4
10 10 5 5
5 6
3 5
7 5
5 3
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
2

说明/提示

数据解释:

第一个数据, 小杨直接往上走,只被 (5,6) 监控过.

第二个数据,小杨被 (7,7) 监控- 走到 (9,9) 被 (7,11) 监控, 然后直接往上走.

数据规模:

前 50%数据. n<=200;

其余数据 n<=600.

求出每个亲戚与其他亲戚的垂直平分线 , 这些垂直平分线以及矩形的四周 , 构成了半平面交的凸包

将每个半平面交求出 , 然后将他与他相邻的亲戚连边 , 边权为 1 , 跑最短路,

这题就是重在建图,也就是求出每个亲戚的管辖范围

注意

1.有一些在矩形外的亲戚

2.特判一下 \(n = 0\) 的情况

3.求交点的函数 , 一定要想清楚 , 实在不行就记住 ,

inline point meetline(Line A , Line B)
{
    if(dcmp(cross(A.v , B.v)) == 0) return (point){0 , 0};
    point k = A.p - B.p;
    return A.p + A.v * (cross(B.v , k) / cross(A.v , B.v));
}

前者减去后者

返回前者乘上比率 , 比率是后者 * k 与总面积的比值

4.onright 函数

传入cross 的是直线的方向而不是直线的端点

return dcmp(cross(A - B.p , B.v))// AC 
return dcmp(cross(A - B.p , B.p))// WA 

5.注意判断条件 是用来 \(if\) 还是 \(continue\)

if(no[i] || j == i) countinue;
if(!no[i] && j != i) { do .... }
6.多测清空

注意 dcmp 的传入参数的类型 一定一定是 double

总之 , 注意细节!!

#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 1000;
const double eps = 1e-8;
inline int read()
{
    register int x = 0; register char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    return x;
}
int n , X0 , Y0 , X1 , Y1 , s , tot , cnt;
int head[N] , no[N];
struct edge{int v , nex;} e[N*N];
inline int dcmp(double x) { return fabs(x) < eps ? 0 : (x < 0 ? -1 : 1); }
void add(int u , int v) 
{ 
    e[++cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt;
    e[++cnt].v = u; e[cnt].nex = head[v]; head[v] = cnt;
    return ;
}
struct point{
    double x , y;
    point(double x = 0 , double y = 0) : x(x) , y(y) {}
    point operator + (const point &A) const {return point(x + A.x , y + A.y);}
    point operator - (const point &A) const {return point(x - A.x , y - A.y);}
    point operator * (const double &K) const { return point(x * K , y * K); }
    double operator * (const point &A) const {return x * A.x + y * A.y; }
}a[N] , p[N];

struct Line{
    point p , v; int id; double ang;
    Line(point p = point() , point v = point() , int id = 0): p(p) , v(v), id(id) { ang = atan2(v.y , v.x); }
    bool operator < (const Line &A) const { return ang < A.ang; }
}l[N] , sta[N];

double cross(point A , point B) { return A.x * B.y - A.y * B.x; }
double dot(point A) { return A.x * A.x + A.y * A.y; }
double dis(point A , point B) { return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));}
inline bool onright(point A , Line B) { return dcmp(cross(A - B.p , B.v)) > 0; } // !!!!
inline point meetline(Line A , Line B)
{
    if(dcmp(cross(A.v , B.v)) == 0) return (point){0 , 0};
    point k = A.p - B.p;
    return A.p + A.v * (cross(B.v , k) / cross(A.v , B.v));
}

point rot(point A) { return point(-A.y , A.x); }
Line get_mid_line(point A , point B , int i) // A 和 B 的垂直平分线
{
    point p = point((A.x + B.x) / 2 , (A.y + B.y) / 2);
    return Line(p , rot(B - A) , i);
}

void init(int id)
{
    tot = 0;
    l[++tot] = Line(point(0 , 0) , point(1 , 0) , n + 1);
    l[++tot] = Line(point(X1 , 0) , point(0 , 1) , n + 1);
    l[++tot] = Line(point(X1 , Y1) , point(-1 , 0) , n + 1);
    l[++tot] = Line(point(0 , Y1) , point(0 , -1) , n + 1);
    for(int i = 1 ; i <= n ; ++i) if(i != id && !no[i]) // !!!
        l[++tot] = get_mid_line(a[id] , a[i] , i);
    return ;
}

void calc(int id)
{
    int n = tot , tail , head;
    sort(l + 1 , l + 1 + n); sta[head = tail = 1] = l[1];
    for(int i = 2 ; i <= n ; ++i)
    {
        while(head < tail && onright(p[tail-1] , l[i])) tail--;
        while(head < tail && onright(p[head] , l[i])) head++;
        sta[++tail] = l[i];
        if(dcmp(cross(sta[tail].v , sta[tail-1].v)) == 0)
        {
            tail--;
            if(onright(sta[tail].p , l[i])) sta[tail] = l[i];
        }
        if(head < tail) p[tail-1] = meetline(sta[tail] , sta[tail-1]);
    }
    while(head < tail && onright(p[tail-1] , sta[head])) tail--;
    if(head >= tail) return ;
    p[tail] = meetline(sta[head] , sta[tail]);
    for(int i = head ; i <= tail ; ++i) add(id , sta[i].id);
    return ;
}

int d[N] , vis[N];
int spfa(int s)
{
    queue q; q.push(s);
    for(int i = 1 ; i <= n + 1 ; ++i) d[i] = 1e9 , vis[i] = 0;
    d[s] = 0;
    while(q.size())
    {
        int x = q.front(); q.pop(); vis[x] = 0;
        for(int i = head[x] ; i ; i = e[i].nex)
        {
            int v = e[i].v;
            if(d[v] > d[x] + 1)
            {
                d[v] = d[x] + 1;
                if(!vis[v]) vis[v] = 1 , q.push(v);
            }
        }
    }
    return d[n+1];
}

void clear() // 多测清空
{
    memset(head , 0 , sizeof head); cnt = 0;
    memset(no , 0 , sizeof no);
    return ;
}

int main()
{
    int T = read();
    while(T --> 0)
    {
        n = read();
        X1 = read(); Y1 = read(); X0 = read(); Y0 = read();
        if(n == 0) { puts("0"); continue; }
        double minn = 1e50 , len = 1e50;
        for(int i = 1 , x , y ; i <= n ; ++i)
        {
            x = read() , y = read() , a[i] = point(1.0 * x , 1.0 * y);
            if(x > X1 || y > Y1) no[i] = 1;
            len = dis(a[i] , point(X0 , Y0));
            if(len < minn) minn = len , s = i;
        }
        //printf("---------------------\n%d\n--------------------------\n" , s);
        for(int i = 1 ; i <= n ; ++i)
        {
            if(no[i]) continue;
            init(i); calc(i);
        }
        printf("%d\n" , spfa(s));
        clear();
    }
    return 0;
}
/*
2
4
10 10 5 5
5 6
3 5
7 5
5 3
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
 */

你可能感兴趣的:(P3297 [SDOI2013]逃考)