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

Description

[北大集训 2021] 魔塔 OL

题目背景

CTT2021 D1T2

题目描述

比特游戏公司最近发布了一款新游戏《魔塔 Online》,玩家可以操控勇士在游戏世界中与怪物进行搏斗。在游戏发布之初,魔塔里没有任何怪物,接下来将依次发生 qq 个事件,每个事件是以下三种之一:

  • + x y z a b:表示游戏发布了新版本,在游戏中新增了一只怪物。如果这是第一只新增的怪物,那么它的编号为 11;否则它的编号为最后一只新增的怪物的编号 +1+1。这只怪物位于魔塔的第 xx 层,它的等级为 yy 级,它的难度为 zz。如果玩家选择击杀这只怪物,那么需要消耗 aa 点血量,在击杀成功后,玩家将得到一支可以恢复 bb 点血量的药剂并立即使用。
  • - k:表示游戏发布了新版本,编号为 kk 的怪物由于平衡性问题下架,它将不会出现在魔塔中。请注意:下架的怪物仍然保留它们的编号,未来新增的怪物不会复用被下架怪物的编号。
  • ? g l d:表示一个询问。某玩家希望击杀魔塔前 gg 层中所有等级不超过 ll 且难度不超过 dd 的怪物。玩家可以按照任意顺序去击杀这些怪物,登上新的一层不需要杀光当前层的所有怪物,且作战过程中不会受到别的怪物的干扰。你的任务是帮助该玩家计算出征前勇士的血量最少是多少。如果某个时刻勇士的血量是负数,那么游戏结束,你一定要防止这种情况的发生。

请写一个程序,依次回答每个询问。注意:每个询问只是玩家的一个思考,不会真正击杀任何一只怪物。

输入数据保证 1q1500001\leq q\leq 150\,000,怪物总数不超过 5000050\,000,询问数量不超过 5000050\,000

对于新增怪物操作,保证 1x,y,z100001\leq x,y,z\leq 10\,000,且 0a,b1090\leq a,b\leq 10^9

对于下架怪物操作,保证操作合法,且每只怪物不会被重复下架。

对于询问,保证 1g,l,d100001\leq g,l,d\leq 10\,000

Solution

考虑怎么单次 O(n)O(n) 做这个事情。

容易发现打完怪物后能回血的一定要放前面,且回血的怪物内部一定是按照 aa 从小到大排序,不回血的怪物按照 bb 从大到小排序,答案就是前缀最小值的相反数。

显然这个东西加上动态三维偏序用 polylog 算法一定过不了,于是考虑优化 O(n2)O(n^2)

先把所有的怪物拿出来排好序,设有 nn 个,询问次数为 mm,则可以用一个 bitset 维护每一维前缀的怪物编号,把三维的前缀与起来就可以得到三维偏序的怪物。

但是这样显然过不了,因为每次更新要改变每维的一个后缀。

考虑分块,设每个块的大小为 BB,每次对于一个 BB 去做上面那个暴力,计算这个块对答案的贡献。

则单次复杂度为 O(2B(n+m+V))O\left(2^B(n+m+V)\right),总复杂度为 O(nB2B(n+m+V))O\left(\frac{n}{B}\cdot 2^B(n+m+V)\right),当 B=lognB=\log n 时取到最小值 O(n(n+m+V)logn)O\left(\frac{n(n+m+V)}{\log n}\right)

因此时间复杂度:O(n(n+m+V)logn)O\left(\frac{n(n+m+V)}{\log n}\right)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#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;
}

UOJ #710. 【北大集训2021】魔塔 OL
https://sobaliuziao.github.io/2024/04/06/post/3955a8c1.html
作者
Egg_laying_master
发布于
2024年4月6日
许可协议