题意:
WJJ喜欢“魔兽争霸”这个游戏。在游戏中,巫妖是一种强大的英雄,它的技能Frozen Nova每次可以杀死一个小精灵。我们认为,巫妖和小精灵都可以看成是平面上的点。
当巫妖和小精灵之间的直线距离不超过R,且巫妖看到小精灵的视线没有被树木阻挡(也就是说,巫妖和小精灵的连线与任何树木都没有公共点)的话,巫妖就可以瞬间杀灭一个小精灵。
在森林里有N个巫妖,每个巫妖释放Frozen Nova之后,都需要等待一段时间,才能再次施放。不同的巫妖有不同的等待时间和施法范围,但相同的是,每次施放都可以杀死一个小精灵。
现在巫妖的头目想知道,若从0时刻开始计算,至少需要花费多少时间,可以杀死所有的小精灵?
sloution
因为求的是最小的时间,考虑二分答案。
我们现在时间确定了,那么每个巫师能打死的小精灵的数量也是确定的。
考虑用网络流求解。
从源点向巫师连一条容量为巫师能打死的小精灵的数量,从每个巫师向他能打到的小精灵连一条容量为 (1) 的边。
再由每个小精灵向汇点连一条容量为 (1) 的边。
这样跑出来的最大流,就是我们最多能打死的精灵的数量。
现在想想,怎么求每个巫师能打死那个精灵。 (n^3) 暴力枚举一下。
因为每棵树的范围是一个圆,所以就相当于问你这个圆是否和巫师和小精灵的连线有交。
可以求出圆心到这条线段的最小距离,在和半径比较即可。
此外,还应该判断小精灵是否在巫师的攻击范围内,即点是否在圆内。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
const double eps = 1e-8;
const int inf = 1e7+10;
const int N = 2010;
int n,m,k,u,v,s,t,res,tot = 1;
int head[N],dep[N],tim[N],r[N],R[N];
bool used[N][N];
struct node
{
int to,net,w;
}e[100010];
struct point
{
int x,y;
point(){}
point(int a,int b){x = a, y = b;}
}c[N],p[N],tr[N];
typedef point Vector;
point operator + (point a,point b){return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b){return point(a.x-b.x,a.y-b.y);}
point operator * (point a,double k){return point(a.x*k,a.y*k);}
double Dot(point a,point b){return a.x*b.x+a.y*b.y;}
double Cro(point a,point b){return a.x*b.y-a.y*b.x;}
double dis(point a,point b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
double dis_PS(point p,point A,point B)
{
if(Dot(p-A,B-A) < 0) return dis(p,A);
if(Dot(p-B,A-B) < 0) return dis(p,B);
return abs(Cro(A-p,B-p))/dis(A,B);
}
void add(int x,int y,int w)
{
e[++tot].to = y;
e[tot].w = w;
e[tot].net = head[x];
head[x] = tot;
}
bool bfs()
{
queue<int> q;
for(int i = 0; i <= t; i++) dep[i] = 0;
q.push(s); dep[s] = 1;
while(!q.empty())
{
int x = q.front(); q.pop();
for(int i = head[x]; i; i = e[i].net)
{
int to = e[i].to;
if(e[i].w && !dep[to])
{
dep[to] = dep[x] + 1;
q.push(to);
if(to == t) return 1;
}
}
}
return 0;
}
int dinic(int x,int flow)
{
if(x == t || !flow) return flow;
int rest = flow, val = 0;
for(int i = head[x]; i && rest; i = e[i].net)
{
int to = e[i].to;
if(!e[i].w || dep[to] != dep[x] + 1) continue;
val = dinic(to,min(rest,e[i].w));
if(val == 0) dep[to] = 0;
e[i].w -= val; e[i^1].w += val; rest -= val;
}
return flow - rest;
}
bool judge(int mid)
{
s = 0, t = n+m+1, tot = 1, res = 0;
memset(head,0,sizeof(head));
for(int i = 1; i <= n; i++) add(s,i,(mid/tim[i])+1), add(i,s,0);
for(int i = 1; i <= m; i++) add(i+n,t,1), add(t,i+n,0);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(used[i][j]) add(i,j+n,1), add(j+n,i,0);
}
}
int flow = 0;
while(bfs())
{
while(flow = dinic(s,inf)) res += flow;
}
return res >= m;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i = 1; i <= n; i++) scanf("%d%d%d%d",&c[i].x,&c[i].y,&r[i],&tim[i]);
for(int i = 1; i <= m; i++) scanf("%d%d",&p[i].x,&p[i].y);
for(int i = 1; i <= k; i++) scanf("%d%d%d",&tr[i].x,&tr[i].y,&R[i]);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
int flag = 0;
double tmp = dis(c[i],p[j]);
if(tmp > r[i]) continue;
for(int l = 1; l <= k; l++)
{
double tmp = dis_PS(tr[l],c[i],p[j]);
if(tmp <= R[l]) flag = 1;
}
if(flag == 0) used[i][j] = 1;
}
}
int L = 0, R = 500000, ans = -1;
while(L <= R)
{
int mid = (L + R)>>1;
if(judge(mid))
{
R = mid - 1;
ans = mid;
}
else L = mid + 1;
}
printf("%d
",ans);
return 0;
}