UOJ #710. 【北大集訓2021】魔塔 OL

下蛋爷發表於2024-04-06

Description

[北大集訓 2021] 魔塔 OL

題目背景

CTT2021 D1T2

題目描述

位元遊戲公司最近釋出了一款新遊戲《魔塔 Online》,玩家可以操控勇士在遊戲世界中與怪物進行搏鬥。在遊戲釋出之初,魔塔裡沒有任何怪物,接下來將依次發生 \(q\) 個事件,每個事件是以下三種之一:

  • + x y z a b:表示遊戲釋出了新版本,在遊戲中新增了一隻怪物。如果這是第一隻新增的怪物,那麼它的編號為 \(1\);否則它的編號為最後一隻新增的怪物的編號 \(+1\)。這隻怪物位於魔塔的第 \(x\) 層,它的等級為 \(y\) 級,它的難度為 \(z\)。如果玩家選擇擊殺這隻怪物,那麼需要消耗 \(a\) 點血量,在擊殺成功後,玩家將得到一支可以恢復 \(b\) 點血量的藥劑並立即使用。
  • - k:表示遊戲釋出了新版本,編號為 \(k\) 的怪物由於平衡性問題下架,它將不會出現在魔塔中。請注意:下架的怪物仍然保留它們的編號,未來新增的怪物不會複用被下架怪物的編號。
  • ? g l d:表示一個詢問。某玩家希望擊殺魔塔前 \(g\) 層中所有等級不超過 \(l\) 且難度不超過 \(d\) 的怪物。玩家可以按照任意順序去擊殺這些怪物,登上新的一層不需要殺光當前層的所有怪物,且作戰過程中不會受到別的怪物的干擾。你的任務是幫助該玩家計算出徵前勇士的血量最少是多少。如果某個時刻勇士的血量是負數,那麼遊戲結束,你一定要防止這種情況的發生。

請寫一個程式,依次回答每個詢問。注意:每個詢問只是玩家的一個思考,不會真正擊殺任何一隻怪物。

輸入資料保證 \(1\leq q\leq 150\,000\),怪物總數不超過 \(50\,000\),詢問數量不超過 \(50\,000\)

對於新增怪物操作,保證 \(1\leq x,y,z\leq 10\,000\),且 \(0\leq a,b\leq 10^9\)

對於下架怪物操作,保證操作合法,且每隻怪物不會被重複下架。

對於詢問,保證 \(1\leq g,l,d\leq 10\,000\)

Solution

考慮怎麼單次 \(O(n)\) 做這個事情。

容易發現打完怪物後能回血的一定要放前面,且回血的怪物內部一定是按照 \(a\) 從小到大排序,不回血的怪物按照 \(b\) 從大到小排序,答案就是字首最小值的相反數。

顯然這個東西加上動態三維偏序用 polylog 演算法一定過不了,於是考慮最佳化 \(O(n^2)\)

先把所有的怪物拿出來排好序,設有 \(n\) 個,詢問次數為 \(m\),則可以用一個 bitset 維護每一維字首的怪物編號,把三維的字首與起來就可以得到三維偏序的怪物。

但是這樣顯然過不了,因為每次更新要改變每維的一個字尾。

考慮分塊,設每個塊的大小為 \(B\),每次對於一個 \(B\) 去做上面那個暴力,計算這個塊對答案的貢獻。

則單次複雜度為 \(O\left(2^B(n+m+V)\right)\),總複雜度為 \(O\left(\frac{n}{B}\cdot 2^B(n+m+V)\right)\),當 \(B=\log n\) 時取到最小值 \(O\left(\frac{n(n+m+V)}{\log n}\right)\)

因此時間複雜度:\(O\left(\frac{n(n+m+V)}{\log n}\right)\)

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 5e4 + 5, kMaxV = 1e4 + 5, kMaxQ = 1.5e5 + 5;

struct Node {
  int x, y, z, a, b, l, r;
} a[kMaxN];

int n, m, q, b;
int x[kMaxN], y[kMaxN], z[kMaxN], t[kMaxN];
int64_t sum[kMaxN], ans[kMaxN];

bool cmp(Node a, Node b) {
  bool fl1 = (a.a > a.b), fl2 = (b.a > b.b);
  if (fl1 != fl2) return fl1 < fl2;
  else if (fl1 == 0) return a.a < b.a;
  else return a.b > b.b;
}

void solve(int l, int r) {
  static int s[kMaxV][3];
  static int64_t sm[kMaxN], mi[kMaxN];
  memset(s, 0, sizeof(s));
  std::vector<std::pair<int, int>> vec;
  for (int i = l; i <= r; ++i) {
    vec.emplace_back(a[i].l, (1 << (i - l)));
    vec.emplace_back(a[i].r, (1 << (i - l)));
    s[a[i].x][0] |= (1 << (i - l));
    s[a[i].y][1] |= (1 << (i - l));
    s[a[i].z][2] |= (1 << (i - l));
  }
  for (int s = 1; s < (1 << (r - l + 1)); ++s) {
    int i = 31 - __builtin_clz(s) + l, t = s ^ (1 << (i - l));
    mi[s] = std::min(mi[t], sm[t] - a[i].a);
    sm[s] = sm[t] - a[i].a + a[i].b;
  }
  for (int i = 1; i < kMaxV; ++i) {
    s[i][0] |= s[i - 1][0];
    s[i][1] |= s[i - 1][1];
    s[i][2] |= s[i - 1][2];
  }
  std::sort(vec.begin(), vec.end());
  int p = 0, now = 0;
  for (int i = 1; i <= m; ++i) {
    for (; p < vec.size() && vec[p].first <= t[i]; ++p)
      now ^= vec[p].second;
    int msk = now & s[x[i]][0] & s[y[i]][1] & s[z[i]][2];
    ans[i] = std::min(ans[i], sum[i] + mi[msk]);
    sum[i] += sm[msk];
  }
}

void dickdreamer() {
  std::cin >> q;
  for (int i = 1; i <= q; ++i) {
    std::string op;
    std::cin >> op;
    if (op[0] == '+') {
      ++n;
      std::cin >> a[n].x >> a[n].y >> a[n].z >> a[n].a >> a[n].b;
      a[n].l = i, a[n].r = q + 1;
    } else if (op[0] == '-') {
      int k;
      std::cin >> k;
      a[k].r = i;
    } else {
      ++m;
      std::cin >> x[m] >> y[m] >> z[m];
      t[m] = i;
    }
  }
  std::sort(a + 1, a + 1 + n, cmp);
  b = std::max(1, std::__lg(n));
  for (int i = 1; i <= n; i += b) solve(i, std::min(i + b - 1, n));    
  for (int i = 1; i <= m; ++i) std::cout << -ans[i] << '\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;
}

相關文章