1. 程式人生 > >【bzoj1822】[JSOI2010]Frozen Nova 冷凍波 計算幾何+二分+網絡流最大流

【bzoj1822】[JSOI2010]Frozen Nova 冷凍波 計算幾何+二分+網絡流最大流

string while mes 遊戲 using font 小精靈 是否 開始

題目描述

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


題解

計算幾何+二分+網絡流最大流

首先要解決的是是否能夠攻擊到,如果兩個點形成的線段與所有圓都沒有公共點,那麽就可以攻擊到。

線段與圓有公共點,需要滿足兩個條件之一:(1)線段某端點在圓內 (2)直線與圓有公共點,且以線段和端點與圓心連線的夾角是銳角。

其中直線與圓有公共點可以使用點到直線距離公式:$\frac{|ax_0+by_0+c|}{\sqrt{a^2+b^2}}$,夾角是銳角可以使用余弦定理的推論:如果$a^2<b^2+c^2$,那麽$A$是銳角。

判斷完以後就是經典的二分+最大流判斷問題了:二分時間,S向巫妖連邊,容量為mid時間攻擊次數;巫妖向能夠攻擊到的小精靈連邊,容量為1;小精靈向T連邊,容量為1。如果滿流則mid可行,否則不可行。

註意攻擊次數的計算公式:$\lfloor\frac{mid}t\rfloor+1$,即開場是沒有冷卻的。(為這個問題糾結了半天= =)

#include <queue>
#include <cstdio>
#include <cstring>
#define N 410
#define M 200010
using namespace std;
typedef long long ll;
queue<int> q;
ll xn[N] , yn[N] , rn[N] , tn[N] , xm[N] , ym[N] , xk[N] , yk[N] , rk[N];
int n , m , k , v[N][N] , head[N] , to[M] , val[M] , next[M] , cnt , s , t , dis[N];
inline ll squ(ll x)
{
	return x * x;
}
bool connect(int a , int b)
{
	if(squ(xn[a] - xm[b]) + squ(yn[a] - ym[b]) > squ(rn[a])) return 0;
	int i;
	for(i = 1 ; i <= k ; i ++ )
		if(squ(xn[a] - xk[i]) + squ(yn[a] - yk[i]) <= squ(rk[i]) || squ(xm[b] - xk[i]) + squ(ym[b] - yk[i]) <= squ(rk[i]) || (
		   squ(yn[a] * (xk[i] - xm[b]) - ym[b] * (xk[i] - xn[a]) - yk[i] * (xn[a] - xm[b])) <= squ(rk[i]) * (squ(xn[a] - xm[b]) + squ(yn[a] - ym[b])) && 
		   squ(xn[a] - xm[b]) + squ(yn[a] - ym[b]) + squ(xn[a] - xk[i]) + squ(yn[a] - yk[i]) >= squ(xm[b] - xk[i]) + squ(ym[b] - yk[i]) && 
		   squ(xn[a] - xm[b]) + squ(yn[a] - ym[b]) + squ(xm[b] - xk[i]) + squ(ym[b] - yk[i]) >= squ(xn[a] - xk[i]) + squ(yn[a] - yk[i])))
			return 0;
	return 1;
}
void add(int x , int y , int z)
{
	to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
	to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
}
bool bfs()
{
	int x , i;
	memset(dis , 0 , sizeof(dis));
	while(!q.empty()) q.pop();
	dis[s] = 1 , q.push(s);
	while(!q.empty())
	{
		x = q.front() , q.pop();
		for(i = head[x] ; i ; i = next[i])
		{
			if(val[i] && !dis[to[i]])
			{
				dis[to[i]] = dis[x] + 1;
				if(to[i] == t) return 1;
				q.push(to[i]);
			}
		}
	}
	return 0;
}
int dinic(int x , int low)
{
	if(x == t) return low;
	int temp = low , i , k;
	for(i = head[x] ; i ; i = next[i])
	{
		if(val[i] && dis[to[i]] == dis[x] + 1)
		{
			k = dinic(to[i] , min(temp , val[i]));
			if(!k) dis[to[i]] = 0;
			val[i] -= k , val[i ^ 1] += k;
			if(!(temp -= k)) break;
		}
	}
	return low - temp;
}
bool judge(int mid)
{
	int i , j , sum = 0;
	memset(head , 0 , sizeof(head)) , cnt = 1;
	for(i = 1 ; i <= n ; i ++ ) add(s , i , mid / tn[i] + 1);
	for(i = 1 ; i <= m ; i ++ ) add(i + n , t , 1);
	for(i = 1 ; i <= n ; i ++ )
		for(j = 1 ; j <= m ; j ++ )
			if(v[i][j])
				add(i , j + n , 1);
	while(bfs()) sum += dinic(s , 1 << 30);
	return sum == m;
}
int main()
{
	int i , j , l = 0 , r = 2000000 , mid , ans = -1;
	scanf("%d%d%d" , &n , &m , &k) , s = 0 , t = n + m + 1;
	for(i = 1 ; i <= n ; i ++ ) scanf("%lld%lld%lld%lld" , &xn[i] , &yn[i] , &rn[i] , &tn[i]);
	for(i = 1 ; i <= m ; i ++ ) scanf("%lld%lld" , &xm[i] , &ym[i]);
	for(i = 1 ; i <= k ; i ++ ) scanf("%lld%lld%lld" , &xk[i] , &yk[i] , &rk[i]);
	for(i = 1 ; i <= n ; i ++ )
		for(j = 1 ; j <= m ; j ++ )
			v[i][j] = connect(i , j);
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(judge(mid)) ans = mid , r = mid - 1;
		else l = mid + 1;
	}
	printf("%d\n" , ans);
	return 0;
}

【bzoj1822】[JSOI2010]Frozen Nova 冷凍波 計算幾何+二分+網絡流最大流