冷冻波
作者:互联网
[JSOI2010]冷冻波
传送门:题目传送门
题目描述
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。
样例 #1
样例输入 #1
2 3 1
-100 0 100 3
100 0 100 5
-100 -10
100 10
110 11
5 5 10
样例输出 #1
5
思路:暴力枚举 + 计算几何 + 二分 + 网络流
暴力枚举
先枚举判断每个精灵都能被谁杀死,用二维数组存状态,
遇到不能被杀死的精灵就输出 -1 结束。
几何
判断过程中先看有没有树:
没树
就直接判断巫妖能不能攻击到精灵。
有树
用点到直线距离算出距离和垂足,判断垂足在不在巫妖和精灵之间;如果不在 就直接判断巫妖能不能攻击到精灵;如果在 比较树的半径和距离;
二分
在0~最短的杀死所有精灵的时间内,杀死精灵数随时间单调递增,则可以二分最短的杀死所有精灵的时间。
网络流检验
符合最大流的流量守恒和容量限定,求最多杀死的精灵数的最大流。
建图:源点到巫妖建边,容量为可杀死精灵数;精灵到汇点建边,容量为 1;巫妖到可杀死的精灵建边,容量为 1;
建完图之后,跑一遍 dinic 检验答案。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define ll long long
const int N=10010,M=200010,inf=1e8;
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,m,k,S,T;
int q[N],h[N],e[M],f[M],ne[M],cur[N],d[N],idx;
bool kill[600][600];
struct node
{
double x,y,r,t;
}lich[N],wisp[M],tree[N];
void add(int a,int b,int c)
{
e[idx]=b,f[idx]=c,ne[idx]=h[a],h[a]=idx++;
e[idx]=a,f[idx]=0,ne[idx]=h[b],h[b]=idx++;
}
bool bfs()
{
int tt=0,hh=0;
memset(d,-1,sizeof d);
q[0]=S,d[S]=0,cur[S]=h[S];
while(hh<=tt)
{
int t=q[hh++];
for(int i=h[t];~i;i=ne[i])
{
int ver=e[i];
if(d[ver]==-1&&f[i])
{
d[ver]=d[t]+1;
cur[ver]=h[ver];
if(ver==T) return true;
q[++tt]=ver;
}
}
}
return false;
}
int find(int u,int limit)
{
if(u==T) return limit;
int flow=0;
for(int i=cur[u];~i&&flow<limit;i=ne[i])
{
cur[u]=i;
int ver=e[i];
if(d[ver]==d[u]+1&&f[i])
{
int t=find(ver,min(limit-flow,f[i]));
if(!t) d[ver]=-1;
f[i]-=t,f[i^1]+=t,flow+=t;
}
}
return flow;
}
int dinic()
{
int ans=0,t;
while(bfs()) ans+=find(S,inf);
return ans;
}
bool check(int mid)
{
memset(h,-1,sizeof h);
S=0;idx=0;T=n+m+1;
for(int i=1;i<=n;i++)
{
add(S,i,mid/lich[i].t+1);//源点到巫妖建边,容量为可杀死的精灵数
}
for(int i=1;i<=m;i++)
{
add(i+n,T,1);//精灵到汇点建边,容量为 1
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(kill[i][j])
{
add(i,j+n,1);//巫妖到可杀死的精灵建边,容量为1
}
}
}
return dinic()>=m;
}
bool pd(node a,node b,node c,bool ok)
{
if(ok)//没树
{
if(a.r*a.r<(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)) return false;// 直接判断能不能攻击到
else true;
}
else//有树
{
if(a.r*a.r<(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)) return false; //先判断能不能攻击到
ll A=b.y-a.y,
B=a.x-b.x,
C=b.x*a.y-a.x*b.y;
if(c.r&&(A*c.x+B*c.y+C)*(A*c.x+B*c.y+C)>=(A*A+B*B)*c.r*c.r) return true;//如果能攻击到再判断树会不会挡住
else
{
int xi=(B*B*c.x-A*B*c.y-A*C),yi=(-A*B*c.x+A*A*c.y-B*C)/(A*A+B*B),temp=(A*A+B*B);//处理树到直线的垂点不在巫妖和精灵之间
if((a.y<yi&&yi<b.y)||(b.y<yi&&yi<a.y)) return false;
else return true;
}
}
}
int main()
{
n=read(),m=read(),k=read();
for(int i=1;i<=n;i++)
{
lich[i].x=read(),lich[i].y=read(),lich[i].r=read(),lich[i].t=read();
}
for(int i=1;i<=m;i++)
{
wisp[i].x=read(),wisp[i].y=read();
}
for(int i=1;i<=k;i++)
{
tree[i].x=read(),tree[i].y=read(),tree[i].r=read();
}
for(int i=1;i<=n;i++)//暴力枚举存储攻击关系
{
for(int j=1;j<=m;j++)
{
bool flag=true,ok=0;
int g;
if(k==0) g=0,ok=1;
else g=1;
for(;g<=k;g++)
{
if(!pd(lich[i],wisp[j],tree[g],ok))
{
flag=false;
break;
}
}
kill[i][j]=flag;
}
}
for(int i=1;i<=m;i++)//如果有精灵死不了就输出-1
{
bool flag=false;
for(int j=1;j<=n;j++)
{
if(kill[j][i]) flag=true;
}
if(!flag)
{
puts("-1");return 0;
}
}
int L=0,R=1e9,mid,res=-1;//二分最短的杀死所有精灵的时间
while(L<R)
{
mid=(L+R)>>1;
if(check(mid)) R=mid;
else L=mid+1;
}
printf("%d\n",R);
return 0;
}
标签:ch,idx,int,精灵,小精灵,杀死,冷冻 来源: https://www.cnblogs.com/watasky/p/16446790.html