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,A2AN
第三行, M 个整数,B1,B2BM
接下来的 M 行,每行 N 个整数,这些整数均为 0 或者 1 。这部分中的第 i 行的第 j 个整数为 0 表示第 i 个激光武器不可以攻击第 j 个巨型机器人,为 1 表示第 i 个激光武器可以攻击第 j 个巨型机器人。

Output

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

Sample Input

2 2
3 10
4 6
0 1
1 1

Sample Output

1.300000

HINT

对于全部的数据, 1N,M50,1Ai105,1Bi1000 ,输入数据保证X军团一定能摧毁Y军团的所有巨型机器人

Solution

azi只会做傻逼题
二分+最大流
每次二分一个答案 x ,如下重新建图:

  • S 往每个武器连边,流量为 x×Bi

    • 每个怪兽往 T 连边,流量为 Ai
    • 如果武器 i 可以攻击怪兽 j i j 连边,流量为 INF
    • #include<bits/stdc++.h>
      using namespace std;
      
      #define N 1000
      #define eps (1e-9)
      #define INF (1e9)
      #define rep(i, a, b) for (int i = a; i <= b; i++)
      #define lb long double
      
      inline int read() {
          int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
          while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
      }
      
      inline void write(int x) {
          if (!x) { putchar('0'); return; } if (x < 0) putchar('-'), x = -x;
          char buf[20] = ""; int top = 0; while (x) buf[++top] = x % 10 + '0', x /= 10; while (top) putchar(buf[top--]);
      }
      
      int n, m;
      int A[N], B[N], sum;
      struct edge { int v, next; lb c; }e[1000005];
      int head[N], tot, S, T;
      bool Map[N][N];
      int dep[N], q[N];
      
      inline void add(int u, int v, lb c) {
          e[++tot] = edge{ v, head[u], c }; head[u] = tot;
          e[++tot] = edge{ u, head[v], 0 }; head[v] = tot;
      }
      
      inline bool bfs() {
          int l = 1, r = 1;
          memset(dep, 0, sizeof dep); q[r] = S, dep[S] = 1;
          while (l <= r) {
              int u = q[l++];
              for (int i = head[u]; i; i = e[i].next) {
                  int v = e[i].v; lb c = e[i].c;
                  if (c < eps || dep[v]) continue;
                  dep[v] = dep[u] + 1, q[++r] = v;
                  if (v == T) return 1;
              }
          }
          return 0;
      }
      
      double dfs(int u, lb dis) {
          if (!(u ^ T) || dis < eps) return dis;
          for (int i = head[u]; i; i = e[i].next) {
              int v = e[i].v; lb c = e[i].c;
              if ((dep[v] ^ dep[u] + 1) || c < eps) continue;
              lb d = dfs(v, min(dis, c));
              if (d < eps) continue;
              e[i].c -= d, e[i ^ 1].c += d;
              return d;
          }
          return 0.0;
      }
      
      bool check(lb x) {
          memset(head, 0, sizeof head); tot = 1;
          rep(i, 1, m) add(S, i, x * B[i]);
          rep(i, 1, n) add(i + m, T, A[i]);
          rep(i, 1, m) rep(j, 1, n) if (Map[i][j]) add(i, j + m, INF);
          lb ans = 0.0;
          while (bfs()) ans += dfs(S, INF);
          return fabs(ans - sum) < eps;
      }
      
      int main() {
          scanf("%d%d", &n, &m); T = n + m + 1;
          rep(i, 1, n) A[i] = read(), sum += A[i];
          rep(i, 1, m) B[i] = read();
          rep(i, 1, m) rep(j, 1, n) Map[i][j] = read();
          lb l = 0.0, r = sum * 1.0;
          while (l + 1e-4 < r) { lb mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; }
          printf("%.4lf", (double)l);
          return 0;
      }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值