题目描述
WJJ喜欢“魔兽争霸”这个游戏。在游戏中,巫妖是一种强大的英雄,它的技能Frozen Nova每次可以杀死一个小精灵。我们认为,巫妖和小精灵都可以看成是平面上的点。
当巫妖和小精灵之间的直线距离不超过R,且巫妖看到小精灵的视线没有被树木阻挡(也就是说,巫妖和小精灵的连线与任何树木都没有公共点)的话,巫妖就可以瞬间杀灭一个小精灵。
在森林里有N个巫妖,每个巫妖释放Frozen Nova之后,都需要等待一段时间,才能再次施放。不同的巫妖有不同的等待时间和施法范围,但相同的是,每次施放都可以杀死一个小精灵。
现在巫妖的头目想知道,若从0时刻开始计算,至少需要花费多少时间,可以杀死所有的小精灵?
输入输出格式
输入格式:
输入文件第一行包含三个整数N、M、K(N,M,K<=200),分别代表巫妖的数量、小精灵的数量和树木的数量。
接下来N行,每行包含四个整数x, y, r, t,分别代表了每个巫妖的坐标、攻击范围和施法间隔(单位为秒)。
再接下来M行,每行两个整数x, y,分别代表了每个小精灵的坐标。
再接下来K行,每行三个整数x, y, r,分别代表了每个树木的坐标。
输入数据中所有坐标范围绝对值不超过10000,半径和施法间隔不超过20000。
输出格式:
输出一行,为消灭所有小精灵的最短时间(以秒计算)。如果永远无法消灭所有的小精灵,则输出-1。
输入输出样例
输入样例
2 3 1
-100 0 100 3
100 0 100 5
-100 -10
100 10
110 11
5 5 10
输出样例
5
解题分析:
首先我们可以 O(N3) O ( N 3 ) 预处理每个巫妖能够打到的精灵数量,若有精灵没有巫妖可以打到直接输出-1。 再建立超级源点、汇点, 将巫妖与源点相连, 将精灵与汇点相连,,跑Dinic即可。
代码如下:
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <iomanip>
#include <queue>
#include <vector>
#define R register
#define W while
#define IN inline
#define gc getchar()
namespace Geometry
{
bool fu;
IN void in (int &x)
{
fu = false;
x = 0; static char c = gc;
W (!isdigit(c))
{
if(c == '-') fu = true;
c = gc;
}
W (isdigit(c))
{
x = (x << 1) + (x << 3), x += c - 48, c = gc;
}
if(fu) x = -x;
}
#define db float
#define EPS 1e-10
struct pt
{
int x, y, r;
};
int man_num, tar_num, tree_num;
pt tree[205], man[205], tar[205];
int halt[205], mx_halt;
bool vis[205];
IN pt operator + (const pt &x, const pt &y){return (pt){x.x + y.x, x.y + y.y};}
IN pt operator - (const pt &x, const pt &y){return (pt){x.x - y.x, x.y - y.y};}
IN db operator * (const pt &x, const pt &y){return x.x * y.y - x.y * y.x;}
IN float dist (const pt &x){return sqrt(x.x * x.x + x.y * x.y);}
IN bool is_in (const pt &it, const pt &tree){return dist(it - tree) - tree.r < -EPS;}
}
namespace Max_flow
{
using std::vector;
using std::min;
using std::queue;
using namespace Geometry;
#define INF 0x7fffff
struct Edge
{
int to, flow;
}edge[100005];
int cnt = -1;
int layer[405];
vector <int> nextt[405];// more than 200 indicates the tars
//402 -> start ; 403 ->end;
IN void addedge(int from, int to)
{
nextt[from].push_back(++cnt);
edge[cnt].to = to;
nextt[to].push_back(++cnt);
edge[cnt].to = from;
}
IN bool BFS(const int &start, const int &endd)
{
memset(layer, 0, sizeof(layer));
layer[start] = 1;
queue <int> q;
q.push(start);
R int now;
W (!q.empty())
{
now = q.front();
q.pop();
for (R int i = nextt[now].size() - 1; i >= 0; --i)
{
if(!edge[nextt[now][i]].flow || layer[edge[nextt[now][i]].to]) continue;
layer[edge[nextt[now][i]].to] = layer[now] + 1;
q.push(edge[nextt[now][i]].to);
}
}
return layer[endd];
}
int DFS(int now, int value)
{
if (now == 403 || !value) return value;
int ret = 0;
int floww;
for (R int i = nextt[now].size() - 1; i >= 0; --i)
{
if (!edge[nextt[now][i]].flow || layer[now] != layer[edge[nextt[now][i]].to] - 1) continue;
int tmp = DFS(edge[nextt[now][i]].to, min(value - ret, edge[nextt[now][i]].flow));
if (!tmp) continue;
edge[nextt[now][i]].flow -= tmp;
edge[nextt[now][i] ^ 1].flow += tmp;
ret += tmp;
if(value == tmp) return ret;
}
return ret;
}
IN bool Dinic()
{
int ans = 0;
W(BFS(402, 403))
{
ans += DFS(402,INF);
// printf("%d\n", ans);
}
if (ans == tar_num) return true;
return false;
}
}
using namespace std;
using namespace Geometry;
using namespace Max_flow;
int main()
{
R int num;
R int i , j, k;
in(man_num), in(tar_num), in(tree_num);
for (i = 1; i <= man_num; ++i)
in(man[i].x), in(man[i].y), in(man[i].r), in(halt[i]);
for (i = 1; i <= tar_num; ++i)
in(tar[i].x), in(tar[i].y);
for (i = 1; i <= tree_num; ++i)
in(tree[i].x), in(tree[i].y), in(tree[i].r);
for (i = 1; i <=man_num; ++i)
{
num = 0;
for (j = 1; j <= tar_num; ++j)
{
if(dist(tar[j] - man[i]) - man[i].r > -EPS) continue;
for (k = 1; k <= tree_num; ++k)
{
if(is_in(man[i], tree[k])) goto e1;
if(is_in(tar[j], tree[k]))
{
printf("-1");
return 0;
}
if(fabs((tar[j] - man[i]) * (tree[k] - tar[j]) / dist(tar[j] - man[i])) - tree[k].r < -EPS)
goto e2;
}
addedge(i, j + 200), vis[j] = true, ++num;
e2:
;
}
mx_halt = max(mx_halt, halt[i] * (num - 1));
e1:
;
}
for (i = 1; i <= tar_num; ++i)
{//判断是否有小精灵不被打到
if(!vis[i])
{
printf("-1");
return 0;
}
}
for (i = tar_num + 200; i >= 201; --i)
{
addedge(i, 403);
}
for (i = 1; i <= man_num; ++i)
{
addedge(402, i);
}
int lef = 0, rig = mx_halt * tar_num, mid;
int anss;
W (lef <= rig)//二分时间搜索
{
// printf("%d %d\n", lef, rig);
mid = (lef + rig) >> 1;
for (i = 0; i <= cnt; i += 2)
{//偶数边为正向边, 所以可以如此重置
edge[i].flow = 1;
edge[i ^ 1].flow = 0;
}
for (i = nextt[402].size() - 1; i >= 0; --i)
{
edge[nextt[402][i]].flow = mid/halt[edge[nextt[402][i]].to] + 1;
}
if(Dinic())
{
anss = mid;
if(lef == rig)
break;
rig = mid - 1;
}
else lef = mid + 1;
}
printf("%d", anss);
return 0;
}
坑点分析
- 卡时间加卡精度, 注意边界判断的小于和小于等于的区分以及常数优化。
- * 网络流加边需要从0边加起!* 因为0 ^ 1 = 1, 所以偶数边是正向边, 从1开始加的话所有边都错位了…(不过洛谷数据是真的水, 加错位了还可以得80分…)