P4048
给定 \(n\) 个巫妖,\(m\) 个精灵,\(k\) 棵树。
三者都可看做二维平面上的点,由坐标表示。
第 \(i\) 棵树的半径为 \(R_i\)。
第 \(i\) 个巫妖每隔时间 \(t_i\) 可以杀死范围 \(r_i\) 内的一个精灵,且两者的连线不穿过任意一棵树。
求杀死所有精灵最短时间,无解输出 -1。
\(n,m,k \le 300,\left\vert x_i \right\vert,\left\vert y_i \right\vert \le 10^4,r_i,R_i \le 2\times 10^4\)
首先看到最短时间,可以想到二分答案。
然后判断每对巫妖和精灵之间能否攻击。
树可以抽象成圆。
- 两点之间距离需小于巫妖攻击半径。
直接求出两点距离比较即可。
- 两点连成的线段要和所有树相离。
求线段到每个圆心的距离,用计算几何解决。
如果距离大于半径就说明线段与圆相离。
如果存在精灵不能被任何一个巫妖攻击到就是无解。
最后是网络流部分,设当前时间为 \(x\)。
源点向每个巫妖连容量为 \(\left\lfloor \dfrac{x}{t_i} \right\rfloor+1\) 的边(\(+1\) 是因为时刻 0 可以攻击)。
每个精灵向汇点连容量为 1 的边。
每个巫妖向其能攻击到的每个精灵连容量为 1 的边。
跑网络流,满流则说明能全部杀死。
时间复杂度:\(O \left(n^3+\log_2 (t_i\times n)\, \text{网络流} \right)\)。
#include <bits/stdc++.h>
#define dd double
using namespace std;
const int N=2e4,INF=2e9;
struct d{dd x,y;d(dd X=0,dd Y=0){x=X;y=Y;}}w[N],j[N],s[N];
struct line{d a,b;};
dd dj(d a,d b){return a.x*b.x+a.y*b.y;}dd cj(d a,d b){return a.x*b.y-a.y*b.x;}
d operator+(d a,d b){return d(a.x+b.x,a.y+b.y);}d operator-(d a,d b){return d(a.x-b.x,a.y-b.y);}
dd len(d a,d b){return sqrt(dj(a-b,a-b));}
dd dis(line l,d p){
if(dj(l.a-p,l.b-p)>0)
return min(len(l.a,p),len(l.b,p));
return abs(cj(p-l.a,l.b-l.a)/len(l.a,l.b));
}int n,m,k,st,ed,l,r=INF-1000,mid,ans=INF,mp[1001][1001];
struct E{int v,w,nt;}e[N<<1];
int fir[N],c=1,rw[N],rt[N],tim[N];int cur[N],d[N];queue <int>q;
void init(){memset(e,0,sizeof(e));memset(fir,0,sizeof(fir));c=1;}
void I(int u,int v,int w){
e[++c]=(E){v,w,fir[u]};fir[u]=c;
e[++c]=(E){u,0,fir[v]};fir[v]=c;
}bool bfs(){
for(int i=0;i<=ed;i++)d[i]=0,cur[i]=fir[i];q.push(st);d[st]=1;while(!q.empty()){
int u=q.front(),V;q.pop();for(int i=fir[u];i;i=e[i].nt)if(!d[V=e[i].v]&&e[i].w)d[V]=d[u]+1,q.push(V);
}return d[ed];
}int dfs(int u,int fl){
if(u==ed)return fl;int ans=0,V,re;
for(int i=cur[u];i;i=e[i].nt){
cur[u]=i;if(d[V=e[i].v]==d[u]+1&&e[i].w){
re=dfs(V,min(fl,e[i].w));e[i].w-=re,e[i^1].w+=re;fl-=re,ans+=re;if(!fl)break;}
}if(!ans)d[u]=0;return ans;
}int dinic(){int ans=0;while(bfs())ans+=dfs(st,INF);return ans;}
bool judge(int x,int y){
if(len(w[x],j[y])>rw[x]) return 0;bool flag=1;
for(int i=1;i<=k;i++)
if(dis((line){w[x],j[y]},s[i])<rt[i]) flag=0;
return flag;
}bool check(int x){
init();
for(int i=1;i<=n;i++) I(st,i,x/tim[i]+1);
for(int i=1;i<=m;i++) I(i+n,ed,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(mp[i][j]) I(i,j+n,1);
return dinic()==m;
}int main(){
freopen("test.in","r",stdin);cin>>n>>m>>k;ed=n+m+1;
for(int i=1;i<=n;i++) cin>>w[i].x>>w[i].y>>rw[i]>>tim[i];
for(int i=1;i<=m;i++) cin>>j[i].x>>j[i].y;
for(int i=1;i<=k;i++) cin>>s[i].x>>s[i].y>>rt[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
d[j]=(mp[i][j]=judge(i,j))?1:d[j];
for(int i=1;i<=m;i++)
if(!d[i]){puts("-1");return 0;}
while(l<=r){
mid=l+r>>1;
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}cout<<ans;return 0;
}