P9528 [JOISC 2022] 蚂蚁与方糖 题解

Description

JOI 君是一个生物学家。他准备对蚂蚁和方糖做一些实验。

JOI 君的实验在一个长度为 \(10^9\) 的木条上进行。这根木条被从左往右放置。木条上距离左端点 \(x\) 的点被称作坐标为 \(x\) 的点。

现在,木条上什么都没有。JOI 君将会进行 \(Q\) 次操作。第 \(i\) 个操作 \((1 \le i \le Q)\) 由三个整数 \(T_i,X_i,A_i\) 描述,表示:

  • \(T_i=1\),JOI 君在坐标为 \(X_i\) 的点处放了 \(A_i\) 个蚂蚁。
  • \(T_i=2\),JOI 君在坐标为 \(X_i\) 的点处放了 \(A_i\) 块方糖。

由于蚂蚁和方糖都很小,所以可能会有一些蚂蚁和方糖放在同一个点上。JOI 君也可能在同一个点执行多次操作。

这次实验中使用的蚂蚁具有「好奇心强」的萌点。具体地,当 JOI 君拍手时,每个蚂蚁会执行以下操作:

  • 如果存在一块方糖与该蚂蚁距离不超过 \(L\),它会选择任意一块并吃掉。

可能存在多个蚂蚁同时吃掉一块方糖的情况。

对于每个 \(k\) \((1\le k \le Q)\),JOI 君想要知道以下问题的答案。

  • 假设 JOI 君在第 \(k\) 次操作后拍了一次手,最多有多少块方糖被至少一个蚂蚁吃掉了?

请写一个程序,对于给定的 JOI 君执行的操作和 \(L\) 的值,对于所有 \(k\) 回答 JOI 君的每个问题。

注意 JOI 君并不会真的拍手。因此蚂蚁的位置不会改变,方糖也不会被吃掉。

对于所有数据,满足:

  • \(1 \le Q \le 500\,000\)
  • \(1 \le L \le 10^9\)
  • \(T_i \in \{1,2\}\)
  • \(0 \le X_i \le 10^9\) \((1 \le i \le Q)\)
  • \(1 \le A_i \le 10^9\) \((1 \le i \le Q)\)

Solution

首先可以转化为二分图最大匹配问题,即对于位置在 \(x\) 的蚂蚁,向 \([x-L,x+R]\) 内的方糖连边,问最大匹配。

跑网络流显然是不行的。这时就要用到 Hall 定理:设左部点集合为 \(A\),右部点集合为 \(B\),则最大匹配为 \(\displaystyle|A|-\max_{S\subseteq A}\{|S|-|\text{Nb}(S)|\}\)

注意到这里的每个左部点连接的是个长度相等的区间,所以用 Hall 定理会很好做。

具体的,选择的左部点集合一定构成若干个区间 \([l_1,r_1],[l_2,r_2],\ldots,[l_k,r_k]\),对应的右部点集合为 \([l_1-L,r_1+L],[l_2-L,r_2+L],\ldots,[l_k-L,r_k+L]\)

容易发现如果存在 \([l_i,r_i]\)\([l_j,r_j]\) 有交或者 \([l_i-L,r_i+L]\)\([l_j-L,r_j+L]\) 有交,那么将其合并一定更优。

所以最终的区间一定满足 \(l_i-r_{i-1}>2L\)

\(p_i=\text{cntant}_{1,i}-\text{cntsugar}_{1,i+L},q_i=\text{cntant}_{1,i-1}-\text{cntsugar}_{1,i-1-L}\),则一个区间 \([l,r]\) 的贡献为 \(p_r-q_l\)


现在问题转化为了选择 \(k\) 个区间,要求最大化 \(\sum_{i=1}^{k}{(p_{r_i}-q_{l_i})}\)。单次询问可以 dp 做。

考虑用线段树维护上面的那个 dp,设 \(f_{x,0/1,0/1}\) 表示 \(x\) 对应的区间,选的第一个为 \(l\) 还是 \(r\),最后一个是 \(l\) 还是 \(r\)

如果在 \(x\) 处加入 \(c\) 个蚂蚁,那么变化为:\([x,+\infty]\)\(p_i\)\(c_i\)\([x+1,+\infty]\)\(q_i\)\(c_i\)。转换一下,变为 \(p_x\)\(c_i\)\([x+1,+\infty]\)\(p_i\)\(q_i\) 同时加 \(c_i\)

前面的是单点修改,后面的 \(4\) 种 dp 最优选法一定不变,打个标记即可。

如果在 \(x\) 处加入 \(c\) 块方糖,变化为:\([x-L,x+L]\)\(p_i\)\(-c_i\)\([x+L+1,+\infty]\)\(p_i\)\(q_i\) 同时加 \(-c_i\)

后面的和上面是一样的。由于最优方案一定满足 \(r_i-l_{i-1}>2L\),所以 \([x-L,x+R]\) 内部的四种 dp 的最优方案选的点数也是确定的,同样打标记即可。

具体见代码。

时间复杂度:\(O(Q\log Q)\)

Code

#include <bits/stdc++.h>

#define int int64_t

using pii = std::pair<int, int>;

const int kMaxN = 5e5 + 5;

int n, m, L;
int op[kMaxN], x[kMaxN], c[kMaxN], unq[kMaxN];

struct Node {
  int LL, LR, RL, RR;
  friend Node operator +(const Node &n1, const Node n2) {
    static Node ret;
    ret.LL = std::max({n1.LL, n2.LL, n1.LL + n2.RL, n1.LR + n2.LL});
    ret.LR = std::max({n1.LR, n2.LR, n1.LL + n2.RR, n1.LR + n2.LR});
    ret.RL = std::max({n1.RL, n2.RL, n1.RL + n2.RL, n1.RR + n2.LL});
    ret.RR = std::max({n1.RR, n2.RR, n1.RL + n2.RR, n1.RR + n2.LR});
    return ret;
  }
};

struct SGT {
  Node t[kMaxN * 4];
  std::array<int, 4> tag[kMaxN * 4];
  void pushup(int x) {
    t[x] = t[x << 1] + t[x << 1 | 1];
  }
  void addtag(int x, std::array<int, 4> v) {
    t[x].LL += v[0], t[x].LR += v[1], t[x].RL += v[2], t[x].RR += v[3];
    tag[x][0] += v[0], tag[x][1] += v[1], tag[x][2] += v[2], tag[x][3] += v[3];
  }
  void pushdown(int x) {
    if (tag[x][0] || tag[x][1] || tag[x][2] || tag[x][3]) {
      addtag(x << 1, tag[x]), addtag(x << 1 | 1, tag[x]);
      tag[x] = {0, 0, 0, 0};
    }
  }
  void update(int x, int l, int r, int ql, int qr, std::array<int, 4> v) {
    if (l > qr || r < ql) return;
    else if (l >= ql && r <= qr) return addtag(x, v);
    pushdown(x);
    int mid = (l + r) >> 1;
    update(x << 1, l, mid, ql, qr, v), update(x << 1 | 1, mid + 1, r, ql, qr, v);
    pushup(x);
  }
} sgt;

int getid(int x) { return std::lower_bound(unq + 1, unq + 1 + m, x) - unq; }

void discrete() {
  std::sort(unq + 1, unq + 1 + m);
  m = std::unique(unq + 1, unq + 1 + m) - (unq + 1);
}

void dickdreamer() {
  std::cin >> n >> L;
  for (int i = 1; i <= n; ++i) {
    std::cin >> op[i] >> x[i] >> c[i];
    unq[++m] = x[i];
  }
  discrete();
  int tot = 0;
  for (int i = 1; i <= n; ++i) {
    if (op[i] == 1) {
      int id = getid(x[i]);
      sgt.update(1, 1, m, id, id, {0, c[i], 0, c[i]});
      sgt.update(1, 1, m, id + 1, m, {-c[i], 0, 0, c[i]});
      tot += c[i];
    } else {
      int id1 = getid(x[i] - L), id2 = getid(x[i] + L + 1);
      sgt.update(1, 1, m, id1, id2 - 1, {0, -c[i], 0, -c[i]});
      sgt.update(1, 1, m, id2, m, {c[i], 0, 0, -c[i]});
    }
    std::cout << tot - std::max<int>(sgt.t[1].LR, 0) << '\n';
  }
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}
posted @ 2025-04-02 17:01  下蛋爷  阅读(96)  评论(0)    收藏  举报