Description [北大集训 2021] 魔塔 OL 题目背景CTT2021 D1T2
题目描述比特游戏公司最近发布了一款新游戏《魔塔 Online》,玩家可以操控勇士在游戏世界中与怪物进行搏斗。在游戏发布之初,魔塔里没有任何怪物,接下来将依次发生 q q q 个事件,每个事件是以下三种之一:
+ x y z a b:表示游戏发布了新版本,在游戏中新增了一只怪物。如果这是第一只新增的怪物,那么它的编号为 1 1 1 ;否则它的编号为最后一只新增的怪物的编号 + 1 +1 + 1 。这只怪物位于魔塔的第 x x x 层,它的等级为 y y y 级,它的难度为 z z z 。如果玩家选择击杀这只怪物,那么需要消耗 a a a 点血量,在击杀成功后,玩家将得到一支可以恢复 b b b 点血量的药剂并立即使用。- k:表示游戏发布了新版本,编号为 k k k 的怪物由于平衡性问题下架,它将不会出现在魔塔中。请注意:下架的怪物仍然保留它们的编号 ,未来新增的怪物不会复用 被下架怪物的编号。? g l d:表示一个询问。某玩家希望击杀魔塔前 g g g 层中所有 等级不超过 l l l 且难度不超过 d d d 的怪物。玩家可以按照任意顺序 去击杀这些怪物,登上新的一层不需要杀光 当前层的所有怪物,且作战过程中不会受到别的怪物的干扰。你的任务是帮助该玩家计算出征前勇士的血量最少 是多少。如果某个时刻勇士的血量是负数 ,那么游戏结束,你一定要防止这种情况的发生。请写一个程序,依次回答每个询问。注意:每个询问只是玩家的一个思考,不会真正击杀 任何一只怪物。
输入数据保证 1 ≤ q ≤ 150 000 1\leq q\leq 150\,000 1 ≤ q ≤ 1 5 0 0 0 0 ,怪物总数不超过 50 000 50\,000 5 0 0 0 0 ,询问数量不超过 50 000 50\,000 5 0 0 0 0 。
对于新增怪物操作,保证 1 ≤ x , y , z ≤ 10 000 1\leq x,y,z\leq 10\,000 1 ≤ x , y , z ≤ 1 0 0 0 0 ,且 0 ≤ a , b ≤ 1 0 9 0\leq a,b\leq 10^9 0 ≤ a , b ≤ 1 0 9 。
对于下架怪物操作,保证操作合法,且每只怪物不会被重复下架。
对于询问,保证 1 ≤ g , l , d ≤ 10 000 1\leq g,l,d\leq 10\,000 1 ≤ g , l , d ≤ 1 0 0 0 0 。
Solution考虑怎么单次 O ( n ) O(n) O ( n ) 做这个事情。
容易发现打完怪物后能回血的一定要放前面,且回血的怪物内部一定是按照 a a a 从小到大排序,不回血的怪物按照 b b b 从大到小排序,答案就是前缀最小值的相反数。
显然这个东西加上动态三维偏序用 polylog 算法一定过不了,于是考虑优化 O ( n 2 ) O(n^2) O ( n 2 ) 。
先把所有的怪物拿出来排好序,设有 n n n 个,询问次数为 m m m ,则可以用一个 bitset 维护每一维前缀的怪物编号,把三维的前缀与起来就可以得到三维偏序的怪物。
但是这样显然过不了,因为每次更新要改变每维的一个后缀。
考虑分块,设每个块的大小为 B B B ,每次对于一个 B B B 去做上面那个暴力,计算这个块对答案的贡献。
则单次复杂度为 O ( 2 B ( n + m + V ) ) O\left(2^B(n+m+V)\right) O ( 2 B ( n + m + V ) ) ,总复杂度为 O ( n B ⋅ 2 B ( n + m + V ) ) O\left(\frac{n}{B}\cdot 2^B(n+m+V)\right) O ( B n ⋅ 2 B ( n + m + V ) ) ,当 B = log n B=\log n B = log n 时取到最小值 O ( n ( n + m + V ) log n ) O\left(\frac{n(n+m+V)}{\log n}\right) O ( l o g n n ( n + m + V ) ) 。
因此时间复杂度:O ( n ( n + m + V ) log n ) O\left(\frac{n(n+m+V)}{\log n}\right) O ( l o g n n ( n + m + V ) ) 。
Code1 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> 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 ; while (T--) dickdreamer (); return 0 ; }