题解:P15482 [CERC2012] Jewel heist
Bc2_Ch1ckenPr1nce · · 题解
Solution
好题。
第一步转化是很显然的,既然不能选全部的
那么我们固定缺少一个颜色
当前情况下,颜色
对于某一个高度
假如两个相邻的颜色为
也就是说,对于一个固定的颜色
高度
- 越来越多宝石进入高度
\le H 的集合; - 越来越多颜色为
c 的宝石变成x 轴上的障碍。
那么,考虑扫描线。
把所有宝石先按
维护一个树状数组表示当前已经加入扫描线的宝石,按
维护一个障碍物集合 set<int> pos[kMaxN] 表示每种颜色已经出现的
因为
假设当前扫描到一个宝石
在颜色
由于此时树状数组内只有高度比 Query(r - 1) - Query(l) 就是高度低于当前层,
讲一个重点,为什么
假设当前高度
如果我们一个一个处理:
- 处理第一个时把它加入障碍;
- 处理第二个时,第一个已经变成障碍了。
但实际我们可以把矩形高度取得比当前
所以,对于同一层 set 和树状数组用来更新答案,再把这一层的所有点插进去。
注意最后还要扫一遍,因为扫完宝石之后树状数组内已经包含所有宝石,此时可以看为
对于每种颜色
代码实现有一个细节:在每个颜色的障碍集合里加入
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int kMaxN = 2e5 + 10;
int n, m, k, tr[kMaxN], b[kMaxN], mx;
set<int> pos[kMaxN];
struct Node {
int x, y, c;
bool operator< (const Node &b) const {
return y < b.y;
}
} a[kMaxN];
int lowbit(int x) {
return (x & (-x));
}
void Modify(int x, int k) {
for (; x <= m; tr[x] += k, x += lowbit(x));
}
int Query(int x) {
int ans = 0;
for (; x; ans += tr[x], x -= lowbit(x));
return ans;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
for (cin >> t; t; -- t) {
cin >> n >> k, mx = 0;
for (int i = 1; i <= n; ++ i) {
cin >> a[i].x >> a[i].y >> a[i].c, b[i] = a[i].x;
}
// 下面都是离散化
sort(b + 1, b + n + 1);
m = unique(b + 1, b + n + 1) - b - 1;
fill(tr, tr + m + 1, 0);
for (int i = 1; i <= n; ++ i) {
a[i].x = lower_bound(b + 1, b + m + 1, a[i].x) - b;
}
sort(a + 1, a + n + 1);
// 插入哨兵
for (int i = 1; i <= k; ++ i) {
pos[i].clear();
pos[i].insert(0), pos[i].insert(m + 1);
}
for (int i = 1, j; i <= n; i = j) {
// 处理相同的 y 的宝石区间 [i, j)
j = i;
for (; j <= n && a[i].y == a[j].y; ++ j);
// 对于相同的 y,先更新答案
for (int k = i, l, r; k < j; ++ k) {
auto it = pos[a[k].c].lower_bound(a[k].x);
l = *prev(it), r = *it;
mx = max(mx, Query(r - 1) - Query(l));
}
for (int k = i; k < j; ++ k) {
pos[a[k].c].insert(a[k].x); // 插入障碍物集合
Modify(a[k].x, 1); // 插入树状数组
}
}
for (int i = 1, lst; i <= k; ++ i) { // 最后还要扫一遍
lst = 0;
for (auto k : pos[i]) {
if (k) {
mx = max(mx, Query(k - 1) - Query(lst)); // 高度无限大,只需要在障碍之间取即可
}
lst = k;
}
}
cout << mx << '\n';
}
return 0;
}