bzoj3993: [SDOI2015]星际战争

Description

3333年,在银河系的某星球上,X军团和Y军团正在激烈地作战。在战斗的某一阶段,Y军团一共派遣了N个巨型机器人进攻X军团的阵地,其中第i个巨型机器人的装甲值为Ai。当一个巨型机器人的装甲值减少到0或者以下时,这个巨型机器人就被摧毁了。X军团有M个激光武器,其中第i个激光武器每秒可以削减一个巨型机器人Bi的装甲值。激光武器的攻击是连续的。这种激光武器非常奇怪,一个激光武器只能攻击一些特定的敌人。Y军团看到自己的巨型机器人被X军团一个一个消灭,他们急需下达更多的指令。为了这个目标,Y军团需要知道X军团最少需要用多长时间才能将Y军团的所有巨型机器人摧毁。但是他们不会计算这个问题,因此向你求助。

Input

第一行,两个整数,N、M。

第二行,N个整数,A1、A2…AN。
第三行,M个整数,B1、B2…BM。
接下来的M行,每行N个整数,这些整数均为0或者1。这部分中的第i行的第j个整数为0表示第i个激光武器不可以攻击第j个巨型机器人,为1表示第i个激光武器可以攻击第j个巨型机器人。
Output

一行,一个实数,表示X军团要摧毁Y军团的所有巨型机器人最少需要的时间。输出结果与标准答案的绝对误差不超过10-3即视为正确。

Sample Input

2 2

3 10

4 6

0 1

1 1
Sample Output

1.300000
HINT

【样例说明1】

战斗开始后的前0.5秒,激光武器1攻击2号巨型机器人,激光武器2攻击1号巨型机器人。1号巨型机器人被完全摧毁,2号巨型机器人还剩余8的装甲值;

接下来的0.8秒,激光武器1、2同时攻击2号巨型机器人。2号巨型机器人被完全摧毁。

对于全部的数据,1<=N, M<=50,1<=Ai<=105,1<=Bi<=1000,输入数据保证X军团一定能摧毁Y军团的所有巨型机器人

题解

不知道为什么,一看题就觉得是二分网络流。。
我们就二分答案x,然后st连向每个炮塔,连x*b的攻击力,炮塔连向他能达到的地方,机器人连向ed,流量是a
看下是否满流就好了

出题人良心,不卡精度,但我为了避免精度,我还是把它转为整数了

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
const LL MAX=1LL<<55;
const LL N=55*2;
const LL ADD=10000;
LL a[N],b[N];//装甲值    攻击力 
LL n,m;
LL c[55][55];//第i个炮可以打到谁
struct qq{LL x,y,z,last;}s[55*56*2];
LL num,last[N];
LL st,ed;
void init (LL x,LL y,LL z)
{
    num++;
    s[num].x=x;s[num].y=y;s[num].z=z;
    s[num].last=last[x];
    last[x]=num;
    swap(x,y);z=0;
    num++;
    s[num].x=x;s[num].y=y;s[num].z=z;
    s[num].last=last[x];
    last[x]=num;
}
LL h[N];
bool Bt ()
{
    memset(h,-1,sizeof(h));h[st]=1;
    queue<int> q;
    q.push(st);
    while (!q.empty())
    {
        LL x=q.front();q.pop();
        for (LL u=last[x];u!=-1;u=s[u].last)
        {
            LL y=s[u].y;
            if (s[u].z>0&&h[y]==-1)
            {
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[ed]!=-1;
}
LL mymin (LL x,LL y){return x<y?x:y;}
LL dfs (LL x,LL f)
{
    if (x==ed) return f;
    LL s1=0;
    for (LL u=last[x];u!=-1;u=s[u].last)
    {
        LL y=s[u].y;
        if (s[u].z>0&&h[y]==h[x]+1&&s1<f)
        {
            LL lalal=dfs(y,mymin(s[u].z,f-s1));
            s1+=lalal;
            s[u].z-=lalal;
            s[u^1].z+=lalal;
        }
    }
    if (s1==0) h[x]=-1;
    return s1;
}
bool check (LL x)
{
    LL shen=0;
    num=1;memset(last,-1,sizeof(last));
    for (LL u=1;u<=n;u++) 
    {
        init(st,u,a[u]);
        shen+=a[u];
    }
    for (LL u=1;u<=m;u++) init(u+n,ed,b[u]*x);
    for (LL u=1;u<=m;u++)
        for (LL i=1;i<=n;i++)
            if (c[u][i])
                init(i,u+n,MAX);
    while (Bt()==true) shen=shen-dfs(st,MAX);
    return shen==0;
}
int main()
{
    scanf("%lld%lld",&n,&m);st=n+m+1;ed=st+1;
    for (LL u=1;u<=n;u++)   
    {
        scanf("%lld",&a[u]);
        a[u]*=ADD;
    }
    for (LL u=1;u<=m;u++)   scanf("%lld",&b[u]);
    for (LL u=1;u<=m;u++)
        for (LL i=1;i<=n;i++)
            scanf("%lld",&c[u][i]);
    LL l=0,r=1000*50*ADD;//时间是多少
    LL ans=0;
    while (l<=r)
    {
        LL mid=(l+r)>>1;
        if (check(mid)) {r=mid-1;ans=mid;}
        else l=mid+1;
    }
    printf("%lf\n",(double)ans/(double)ADD);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值