[BZOJ4548]小奇的糖果
[BZOJ4548]小奇的糖果
试题描述
有 \(N\) 个彩色糖果在平面上。小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果。求出最多能够拾起多少糖果,使得获得的糖果并不包含所有的颜色。
输入
包含多组测试数据,第一行输入一个正整数 \(T\) 表示测试数据组数。
接下来 \(T\) 组测试数据,对于每组测试数据,第一行输入两个正整数 \(N\)、\(K\),分别表示点数和颜色数。
接下来 \(N\) 行,每行描述一个点,前两个数 \(x, y (|x|, |y| \le 2^{30} - 1)\) 描述点的位置,最后一个数 \(z (1 \le z ≤ k)\) 描述点的颜色。
输出
对于每组数据在一行内输出一个非负整数 \(ans\),表示答案
输入示例
1
10 3
1 2 3
2 1 1
2 4 2
3 5 3
4 4 2
5 1 2
6 3 1
6 7 1
7 2 3
9 4 2
输出示例
5
数据规模及约定
对于 \(100\%\) 的数据,\(N \le 100000,K \le 100000,T \le 3\)
题解
此题和那道题是双倍经验关系。
然而我那道题的做法在这里直接 T 飞了,所以再讲一个不那么慢的做法。(虽然也是基本垫底)
如果给出的点没有提到所有颜色,就直接输出 \(n\)。
枚举不能包含的那个颜色 \(c\),令所有颜色为 \(c\) 的点的集和为 \(S_c\)。将 \(S_c\) 中的点按 \(x\) 坐标排序,然后我们从左到右依次考虑每个点,并以 \(y\) 坐标为关键字维护单调栈,当删除单调栈中的元素时统计一下极大子矩形的答案。询问矩形内部点数可以用主席树一个 \(\log n\) 做。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 100010
#define maxnode 2000010
#define pii pair <int, int>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)
int n, K, nX[maxn], nY[maxn];
pii ps[maxn];
vector <pii > col[maxn];
bool cmpy(pii a, pii b) { return a.y < b.y; }
int ToT, rt[maxn], sumv[maxnode], lc[maxnode], rc[maxnode];
void update(int& y, int x, int l, int r, int p) {
sumv[y = ++ToT] = sumv[x] + 1;
if(l == r) return ;
int mid = l + r >> 1; lc[y] = lc[x]; rc[y] = rc[x];
if(p <= mid) update(lc[y], lc[x], l, mid, p);
else update(rc[y], rc[x], mid + 1, r, p);
return ;
}
int query(int o, int l, int r, int ql, int qr) {
if(!o) return 0;
if(ql <= l && r <= qr) return sumv[o];
int mid = l + r >> 1, ans = 0;
if(ql <= mid) ans += query(lc[o], l, mid, ql, qr);
if(qr > mid) ans += query(rc[o], mid + 1, r, ql, qr);
return ans;
}
int Query(int xl, int xr, int yl, int yr) {
if(xl > xr || yl > yr) return 0;
return query(rt[yr], 1, n, xl, xr) - query(rt[yl-1], 1, n, xl, xr);
}
int S[maxn], top;
void work() {
ToT = 0;
memset(rt, 0, sizeof(rt));
memset(lc, 0, sizeof(lc));
memset(rc, 0, sizeof(rc));
n = read(); K = read();
rep(c, 1, K) col[c].clear();
rep(i, 1, n) {
int x = read(), y = read(), c = read();
nX[i] = x; nY[i] = y;
ps[i] = mp(x, y);
col[c].push_back(ps[i]);
}
sort(nX + 1, nX + n + 1);
sort(nY + 1, nY + n + 1);
rep(i, 1, n)
ps[i].x = lower_bound(nX + 1, nX + n + 1, ps[i].x) - nX,
ps[i].y = lower_bound(nY + 1, nY + n + 1, ps[i].y) - nY;
rep(c, 1, K) {
if(!col[c].size()) return (void)printf("%d\n", n);
rep(i, 0, (int)col[c].size() - 1) {
pii& p = col[c][i];
p.x = lower_bound(nX + 1, nX + n + 1, p.x) - nX;
p.y = lower_bound(nY + 1, nY + n + 1, p.y) - nY;
}
}
sort(ps + 1, ps + n + 1, cmpy);
int j = 1;
rep(i, 1, n) {
rt[i] = rt[i-1];
while(j <= n && ps[j].y == i) update(rt[i], rt[i], 1, n, ps[j++].x);
}
int ans = 0;
rep(c, 1, K) {
sort(col[c].begin(), col[c].end());
// optimus
rep(i, 0, (int)col[c].size() - 1) ans = max(ans, Query(i ? col[c][i-1].x + 1 : 1, col[c][i].x - 1, 1, n));
ans = max(ans, Query(col[c][col[c].size()-1].x + 1, n, 1, n));
// down
rep(i, 0, (int)col[c].size() - 1) {
pii now = col[c][i];
while(top && col[c][S[top]].y > now.y) {
ans = max(ans, Query(top > 1 ? col[c][S[top-1]].x + 1 : 1, now.x - 1, 1, col[c][S[top]].y - 1));
top--;
}
S[++top] = i;
}
while(top) {
ans = max(ans, Query(top > 1 ? col[c][S[top-1]].x + 1 : 1, n, 1, col[c][S[top]].y - 1));
top--;
}
// up
rep(i, 0, (int)col[c].size() - 1) {
pii now = col[c][i];
while(top && col[c][S[top]].y < now.y) {
ans = max(ans, Query(top > 1 ? col[c][S[top-1]].x + 1 : 1, now.x - 1, col[c][S[top]].y + 1, n));
top--;
}
S[++top] = i;
}
while(top) {
ans = max(ans, Query(top > 1 ? col[c][S[top-1]].x + 1 : 1, n, col[c][S[top]].y + 1, n));
top--;
}
}
printf("%d\n", ans);
return ;
}
int main() {
int T = read();
while(T--) work();
return 0;
}