P4048 【[JSOI2010]冷冻波】

题意:

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;
}
原文地址:https://www.cnblogs.com/genshy/p/14350657.html