bzoj1822: [JSOI2010]Frozen Nova 冷冻波

21 篇文章 0 订阅
11 篇文章 0 订阅

传送门
先用计算几何判断某个巫师是否能够施法到某个小精灵。
然后二分答案,用网络流验证就可以了。

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#define eps 1e-10
#define inf 0x3f3f3f3f
#define N 405
using namespace std;
struct edge{int to,f,next;}e[N*N];
struct point{int x,y;}a[N],b[N],c[N];
int q[N],head[N],d[N],mp[205][205],tim[205],cr[205],R[205];
int n,m,S,T,l,r,mid,ans,mx,tot,p;
double sqr(double x){return x*x;}
double dis(point a,point b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
int jud(int x,int y){
    if (dis(a[x],b[y])-R[x]>eps) return 0;
    double u,v,w,s,pp,h,t;
    for (int i=1;i<=p;i++){
        u=dis(a[x],b[y]);
        v=dis(a[x],c[i]);
        w=dis(b[y],c[i]);
        pp=(u+v+w)/2;
        s=sqrt(pp*(pp-u)*(pp-v)*(pp-w));
        h=2*s/u;
        t=sqrt(sqr(max(v,w))-h*h);
        if (u-t>eps&&cr[i]-h>eps) return 0;
    }
    return 1;
}
void add(int x,int y,int u){
    e[++tot].to=y;
    e[tot].f=u;
    e[tot].next=head[x];
    head[x]=tot;
}
void ins(int x,int y,int u){add(x,y,u);add(y,x,0);}
void build(int x){
    memset(head,0,sizeof(head));
    tot=1;
    for (int i=1;i<=n;i++) ins(S,i,x/tim[i]+1);
    for (int i=1;i<=m;i++) ins(i+n,T,1);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            if (mp[i][j]) ins(i,j+n,1);
}
bool bfs(){
    int h=0,t=1,x;
    memset(d,-1,sizeof(d));
    d[S]=0;
    q[1]=S;
    while (h<t){
        x=q[++h];
        for (int i=head[x];i;i=e[i].next)
            if (e[i].f&&d[e[i].to]==-1){
                d[e[i].to]=d[x]+1;
                q[++t]=e[i].to;
            }
    }
    return d[T]!=-1;
}
int dinic(int x,int flow){
    if (x==T) return flow;
    int rest=flow,k;
    for (int i=head[x];i&&rest;i=e[i].next)
        if (e[i].f&&d[e[i].to]==d[x]+1){
            k=dinic(e[i].to,min(rest,e[i].f));
            e[i].f-=k;
            e[i^1].f+=k;
            rest-=k;
        }
    if (rest) d[x]=-1;
    return flow-rest;
}
int main(){
    scanf("%d%d%d",&n,&m,&p);
    S=n+m+1,T=S+1; 
    for (int i=1;i<=n;i++){
        scanf("%d%d%d%d",&a[i].x,&a[i].y,&R[i],&tim[i]);
        mx=max(mx,tim[i]);
    }
    for (int i=1;i<=m;i++) scanf("%d%d",&b[i].x,&b[i].y);
    for (int i=1;i<=p;i++) scanf("%d%d%d",&c[i].x,&c[i].y,&cr[i]);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++){
            mp[i][j]=jud(i,j);
            if (mp[i][j]) d[j]=1;
        }
    for (int i=1;i<=m;i++) if (!d[i]){printf("-1"); return 0;}
    l=0,r=m*mx;
    while (l<r){
        mid=(l+r)/2;
        build(mid);
        ans=0;
        while (bfs()) ans+=dinic(S,inf);
        if (ans==m) r=mid; else l=mid+1;
    }
    printf("%d",l);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值