主要思路
\(\Theta(n^2)\)暴力还是很好写的,先枚举水平线,然后再分上下讨论;讨论中,我们都要从左往右枚举,但是我们有两个指针\(lptr, rptr\),我们可以在过程中维护在\([lptr, rptr]\)之间的颜色数始终严格小于\(k\),然后再用点个数更新答案。
那么正解就没这么好写了。我们考虑在\(k\)中枚举一个被排除的颜色,然后分情况讨论:
- 按\(x\)排序,相邻点对做垂直线,两线之间的点个数可能成为答案。
- 向上看,我们可以把点按\(y\)降序排序,然后让矩形的横线段\([l, r]\)强行\(\in x_i\),也就是置横线段为当前点之上。用 multiset 查询当前点前驱后继的\(x\)作为矩形的横线范围,然后把当前点的\(y + 1\)作为基准线,用主席树查询当前矩形内的点数。
- 向下看类似。
详细见代码:
// FOJ3945.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 4e5 + 200; int T, n, k, roots[MAX_N], yupper, gans; struct point { int x, y, c; bool operator<(const point &rhs) const { return x < rhs.x || (x == rhs.x && y < rhs.y); } } pts[MAX_N]; typedef multiset<point>::iterator mpit; bool compareByY(const point &rhs1, const point &rhs2) { return rhs1.y < rhs2.y || (rhs1.y == rhs2.y && rhs1.x < rhs2.x); } vector<point> ptByColor[MAX_N]; vector<int> mpx, mpy; inline char nc() { static char buf[10000000], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 10000000, stdin), p1 == p2) ? EOF : *p1++; } int read() { int x = 0, f = 1; char ch = nc(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = nc(); } while (isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = nc(); return x * f; } namespace PST { struct node { int lson, rson, val; } nodes[MAX_N << 5]; int ptot; int update(int qx, int l, int r, int pre) { int p = ++ptot; nodes[p] = nodes[pre], nodes[p].val++; if (l == r) return p; int mid = (l + r) >> 1; if (qx <= mid) nodes[p].lson = update(qx, l, mid, nodes[pre].lson); else nodes[p].rson = update(qx, mid + 1, r, nodes[pre].rson); return p; } int query(int ql, int qr, int l, int r, int pre, int p) { if (ql > qr) return 0; if (ql <= l && r <= qr) return nodes[p].val - nodes[pre].val; int mid = (l + r) >> 1, ret = 0; if (ql <= mid) ret += query(ql, qr, l, mid, nodes[pre].lson, nodes[p].lson); if (mid < qr) ret += query(ql, qr, mid + 1, r, nodes[pre].rson, nodes[p].rson); return ret; } void init() { ptot = 0; int last_ptr = 0; for (int i = 1, r = 0; i <= n; i = r + 1) { r = i, roots[pts[i].x] = update(pts[i].y, 1, yupper, last_ptr), last_ptr = roots[pts[i].x]; while (r + 1 <= n && pts[r + 1].x == pts[i].x) r++, roots[pts[i].x] = update(pts[r].y, 1, yupper, last_ptr), last_ptr = roots[pts[i].x]; } } } // namespace PST int ripe(vector<int> &vec, int x) { return lower_bound(vec.begin(), vec.end(), x) - vec.begin() + 1; } void processCategory(int color) { using namespace PST; if (ptByColor[color].empty()) return (void)(gans = max(gans, n)); sort(ptByColor[color].begin(), ptByColor[color].end()); // horizontal intervals; gans = max(gans, query(1, yupper, 1, yupper, 0, roots[ptByColor[color].front().x - 1])); for (int i = 1, siz = ptByColor[color].size(); i < siz; i++) { point pt = ptByColor[color][i]; gans = max(gans, query(1, yupper, 1, yupper, roots[ptByColor[color][i - 1].x], roots[pt.x - 1])); continue; } gans = max(gans, query(1, yupper, 1, yupper, roots[ptByColor[color].back().x], roots[mpx.size()])); // looking up; sort(ptByColor[color].begin(), ptByColor[color].end(), compareByY); multiset<point> ms; for (int siz = ptByColor[color].size(), i = siz - 1; i >= 0; i--) { mpit curt = ms.insert(ptByColor[color][i]); int baseLine = ptByColor[color][i].y + 1; int l, r; if (curt != ms.begin()) curt--, l = curt->x, curt++; else l = 0; curt++; if (curt != ms.end()) r = curt->x - 1; else r = mpx.size(); curt--; gans = max(gans, query(baseLine, yupper, 1, yupper, roots[l], roots[r])); } ms.clear(); for (int siz = ptByColor[color].size(), i = 0; i < siz; i++) { mpit curt = ms.insert(ptByColor[color][i]); int baseLine = ptByColor[color][i].y - 1; int l, r; if (curt != ms.begin()) curt--, l = curt->x, curt++; else l = 0; curt++; if (curt != ms.end()) r = curt->x - 1; else r = mpx.size(); curt--; gans = max(gans, query(1, baseLine, 1, yupper, roots[l], roots[r])); } } int main() { T = read(); while (T--) { n = read(), k = read(); mpx.clear(), mpy.clear(), gans = 0; for (int i = 1; i <= n; i++) { pts[i].x = read(), pts[i].y = read(), pts[i].c = read(); mpx.push_back(pts[i].x), mpy.push_back(pts[i].y); } sort(mpx.begin(), mpx.end()), mpx.erase(unique(mpx.begin(), mpx.end()), mpx.end()); sort(mpy.begin(), mpy.end()), mpy.erase(unique(mpy.begin(), mpy.end()), mpy.end()); yupper = mpy.size(); for (int i = 1; i <= k; i++) ptByColor[i].clear(); for (int i = 1; i <= n; i++) { pts[i].x = ripe(mpx, pts[i].x), pts[i].y = ripe(mpy, pts[i].y); ptByColor[pts[i].c].push_back(pts[i]); } sort(pts + 1, pts + 1 + n), PST::init(); for (int i = 1; i <= k; i++) processCategory(i); printf("%d\n", gans); } return 0; }