bzoj1822: [JSOI2010]Frozen Nova 冷冻波 计算几何:直线与圆的交点 最大流 二分答案

bzoj1822: [JSOI2010]Frozen Nova 冷冻波

Description

WJJ喜欢“魔兽争霸”这个游戏。在游戏中,巫妖是一种强大的英雄,它的技能Frozen Nova每次可以杀死一个小精灵。我们认为,巫妖和小精灵都可以看成是平面上的点。 当巫妖和小精灵之间的直线距离不超过R,且巫妖看到小精灵的视线没有被树木阻挡(也就是说,巫妖和小精灵的连线与任何树木都没有公共点)的话,巫妖就可以瞬间杀灭一个小精灵。 在森林里有N个巫妖,每个巫妖释放Frozen Nova之后,都需要等待一段时间,才能再次施放。不同的巫妖有不同的等待时间和施法范围,但相同的是,每次施放都可以杀死一个小精灵。 现在巫妖的头目想知道,若从0时刻开始计算,至少需要花费多少时间,可以杀死所有的小精灵?

Input

输入文件第一行包含三个整数N、M、K(N,M,K<=200),分别代表巫妖的数量、小精灵的数量和树木的数量。 接下来N行,每行包含四个整数x, y, r, t,分别代表了每个巫妖的坐标、攻击范围和施法间隔(单位为秒)。 再接下来M行,每行两个整数x, y,分别代表了每个小精灵的坐标。 再接下来K行,每行三个整数x, y, r,分别代表了每个树木的坐标。 输入数据中所有坐标范围绝对值不超过10000,半径和施法间隔不超过20000。

Output

输出一行,为消灭所有小精灵的最短时间(以秒计算)。如果永远无法消灭所有的小精灵,则输出-1。

Sample Input

2 3 1
-100 0 100 3
100 0 100 5
-100 -10
100 10
110 11
5 5 10

Sample Output

5

分析

计算几何与网络流的一个小结合,挺有意思的。
首先考虑网络流。对于巫妖的施法时间,肯定是要二分答案的。建图的话就是源点到巫妖,流量为巫妖的施法次数,每个巫妖连到可以杀死的小精灵,小精灵连到汇点容量都是1,判断有没有流满小精灵就可以了。
至于巫妖可否杀死小精灵的判断,就是判断某条线段于圆有没有交点。
那么我们计算线段到圆心最短距离和半径比就可以了。
但是注意是线段不是直径。所以我们首先判断垂直的那个店是否在线段上。
判断方法用余弦定理判断圆心和线段两个端点的夹角是锐角直角还是钝角。如果是钝角的话直接把端点到圆心短的那条和半径比。
否则的话叉积求面积除以底边就是高了,也就是我们要的距离,仍然和半径比。
结束它
没有卡精度,shuffle

代码

/**************************************************************
    Problem: 1822
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:88 ms
    Memory:3944 kb
****************************************************************/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int M = 220000, N = 1100, inf = 0x3f3f3f3f;
const double eps = 1e-8;
int read() {
    char ch = getchar();int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f; 
}
struct P {
    int x, y;
    P(int a = 0, int b = 0) : x(a), y(b) {}
    int operator * (P a) {return x * a.y - y * a.x;}
    int operator ^ (P a) {return x * a.x + y * a.y;}
    P operator - (P a) {return P(x - a.x, y - a.y);}
}L[N], F[N], C[N];
int Lr[N], Cr[N], Ti[N], b1, b2;
int sqr(int a) {return  a * a;}
int sqr(P a) {return a ^ a;}
int to[M], nxt[M], w[M], pre[N], h[N], cur[N], gap[N], Q[N], n, m, k, top = 1, s, t;
bool vis[N];
void add(int u, int v, int ww) {to[++top] = v; w[top] = ww; nxt[top] = pre[u]; pre[u] = top;}
void adds(int u, int v, int w) {add(u, v, w); add(v, u, 0);}

bool bfs() {
    for(int i = 0;i <= t + 2; ++i) h[i] = -1, gap[i] = 0;
    ++gap[++h[t]]; int L = 0, R; Q[R = 1] = t;
    while(L < R) {
        int u = Q[++L];
        for(int i = pre[u]; i; i = nxt[i]) 
            if(!(~h[to[i]])) {
                ++gap[h[to[i]] = h[u] + 1];
                Q[++R] = to[i];
            }
    }
    return ~h[s];
}

int dfs(int u, int minf) {
    if(u == t) return minf;
    int flow = 0, f;
    for(int i = cur[u]; i; i = nxt[i]) 
        if(w[i] && h[u] == h[to[i]] + 1) {
            f = min(w[i], minf - flow);
            f = dfs(to[i], f);
            flow += f; w[i] -= f; w[i ^ 1] += f;
            if(w[i]) cur[u] = i;
            if(minf == flow) return flow;
        }
    if(!(--gap[h[u]])) h[s] = t + 2;
    ++gap[++h[u]];
    cur[u] = pre[u];
    return flow;
}

int sap() {
    if(!bfs()) return 0; int ans = 0;
    for(int i = 0;i <= t; ++i) cur[i] = pre[i];
    while(h[s] < t + 2) ans += dfs(s, inf);
    return ans;
}

bool check(int u, int v) {
    int a = sqr(L[u] - F[v]);
    if(a > Lr[u]) return false; 
    double d;
    for(int i = 1;i <= k; ++i) {
        int b = sqr(C[i] - L[u]), c = sqr(C[i] - F[v]);
        if(b > c) swap(b, c);
        if(a + b < c) d = sqrt(b);
        else d = fabs((L[u] - C[i]) * (F[v] - C[i]) * 1.0) / sqrt(a);
        if(d < Cr[i]) return false;
    }
    return true;
}

void Pre() {
    for(int i = 1;i <= n; ++i)
        for(int j = 1;j <= m; ++j)
        if(check(i, j)) adds(i, j + n, 1);
    b1 = top;
}

void rebuild(int mid) {
    for(int i = 2;i <= b1; i += 2) w[i] = 1, w[i + 1] = 0;
    for(int i = b1 + 1; i <= b2; i += 2) w[i] = 1, w[i + 1] = 0;
    for(int i = 1;i <= n; ++i) w[b2 + (i << 1) - 1] = mid / Ti[i] + 1, w[b2 + (i << 1)] = 0;
}

void binary() {
    int L = 0, R = 1e9, ans = -1;
    while(L <= R) {
        int mid = L + R >> 1;
        rebuild(mid);
        if(sap() == m) {
            ans = mid; R = mid - 1;
        }
        else L = mid + 1;
    }
    printf("%d\n", ans);
}

int main() {
    n = read(); m = read(); k = read(); s = n + m + 1; t = s + 1;
    for(int i = 1;i <= n; ++i) {L[i].x = read(); L[i].y = read(); Lr[i] = sqr(read()); Ti[i] = read();}
    for(int i = 1;i <= m; ++i) {F[i].x = read(); F[i].y = read();}
    for(int i = 1;i <= k; ++i) {C[i].x = read(); C[i].y = read(); Cr[i] = read();}
    Pre(); 
    for(int i = 1;i <= m; ++i) adds(i + n, t, 0);
    b2 = top;
    for(int i = 1;i <= n; ++i) adds(s, i, 0);
    binary();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值