P4602 [CTSC2018]混合果汁

共有 n n n种果汁,第 i i i种果汁的美味度为 d i d_i di,每升价格为 p i p_i pi,在一瓶混合果汁中,最多只能添加 l i l_i li升。

m m m个询问,每次询问给出两个数 g , L g, L g,L,我们要找出价格不大于 g g g,体积不小于 L L L的混合果汁的最大美味度。

混合果汁的美味度为所有参与混合的果汁的最小值,如果没有满足条件的混合果汁则输出 − 1 -1 1

把果汁按照美味度排一个序,对每个询问二分枚举 d i d_i di,然后judge [ i , n ] [i, n] [i,n]上的果汁是否可以混合得到满足要求,

当美味度最小值确定了,我们如何挑选果汁,不难想到,肯定是优先选价格更低的,然后次小的,依次类推,

我们按照美味值的相对顺序排序,然后以价格为下标建立主席树,这样我们就只要在 [ m i d + 1 , n ] [mid + 1, n] [mid+1,n]区间的主席树上去判断何不合法就行,

单次求解的复杂度是 log ⁡ 2 n \log ^ 2 n log2n的,感觉好像用不用整体二分效率应该是差不多的吧,整体复杂度都是 O log ⁡ 2 n O \log ^ 2 n Olog2n的。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10, maxn = 100000;

int n, m, num, a[N], ans[N], root[N], ls[N << 5], rs[N << 5];

long long sum[N << 5], cost[N << 5];

struct Res {
  int d, p, l;

  void read() {
    scanf("%d %d %d", &d, &p, &l);
  }

  bool operator < (const Res &t) const {
    return d < t.d;
  }
}s[N];

struct Ans {
  int id;

  long long g, l;
}q[N], q1[N], q2[N];

void update(int &rt, int pre, int l, int r, int x, int v) {
  rt = ++num;
  ls[rt] = ls[pre], rs[rt] = rs[pre], sum[rt] = sum[pre] + v, cost[rt] = cost[pre] + 1ll * x * v;
  if (l == r) {
    return ;
  }
  int mid = l + r >> 1;
  if (x <= mid) {
    update(ls[rt], ls[pre], l, mid, x, v);
  }
  else {
    update(rs[rt], rs[pre], mid + 1, r, x, v);
  }
}

bool judge(int rt1, int rt2, int l, int r, long long pre_G, long long pre_L, long long G, long long L) {
  if (l == r) {
    if (pre_L + sum[rt2] - sum[rt1] < L) {
      return false;
    }
    return pre_G + (L - pre_L) * l <= G;
  }
  int mid = l + r >> 1;
  if (pre_L + sum[ls[rt2]] - sum[ls[rt1]] >= L) {
    return judge(ls[rt1], ls[rt2], l, mid, pre_G, pre_L, G, L);
  }
  else {
    pre_L += sum[ls[rt2]] - sum[ls[rt1]];
    pre_G += cost[ls[rt2]] - cost[ls[rt1]];
    return judge(rs[rt1], rs[rt2], mid + 1, r, pre_G, pre_L, G, L);
  }
}

void solve(int l, int r, int L, int R) {
  if (l > r | L > R) {
    return ;
  }
  if (l == r) {
    for (int i = L; i <= R; i++) {
      ans[q[i].id] = l;
    }
    return ;
  }
  int mid = l + r >> 1, cnt1 = 0, cnt2 = 0;
  for (int i = L; i <= R; i++) {
    if (judge(root[mid], root[n], 1, maxn, 0, 0, q[i].g, q[i].l)) {
      q2[++cnt2] = q[i];
    }
    else {
      q1[++cnt1] = q[i];
    }
  }
  for (int i = 1; i <= cnt1; i++) {
    q[L + i - 1] = q1[i];
  }
  for (int i = 1; i <= cnt2; i++) {
    q[L + cnt1 + i - 1] = q2[i];
  }
  solve(l, mid, L, L + cnt1 - 1), solve(mid + 1, r, L + cnt1, R);
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  scanf("%d %d", &n, &m);
  a[1] = -1;
  for (int i = 2; i <= n + 1; i++) {
    s[i].read();
    a[i] = s[i].d;
  }
  n++;
  sort(a + 1, a + 1 + n);
  sort(s + 1, s + 1 + n);
  for (int i = 2; i <= n; i++) {
    update(root[i], root[i - 1], 1, maxn, s[i].p, s[i].l);
  }
  for (int i = 1; i <= m; i++) {
    long long g, l;
    scanf("%lld %lld", &g, &l);
    q[i] = {i, g, l};
  }
  solve(1, n, 1, m);
  for (int i = 1; i <= m; i++) {
    printf("%d\n", a[ans[i]]);
  }
  return 0;
}