F. Choose a Square
题意:
二维平面上给出\(n\)个点,每个点都有个权值。现在要求选择一个正方形,满足其中一根对角线的两顶点在\(y=x\)这条直线上。
问所选正方形包含了的点的权值和减去边长的最大值。
类似于这样:

答案为\(4\)。
思路:
- 因为正方形具有对称性,所以我们可以考虑将\(y=x\)这条直线下方的点对称过去处理,那么我们现在就相当于选择一个等腰直角三角形的区域,类似于这样:
- 注意到最终三角形的边上一定存在至少一个点,那么也就是说有用的横纵坐标就为这些点的坐标。
- 直接枚举\(x,y\)显然复杂度不能承受,考虑当我们枚举\(x\)时,选择一个最大的\(y\),就类似于维护一个区间最大值。
- 对于一个位置\(x=x_0\),显然随着\(y\)增大包含的点越多,并且对于一个\(y=y_0\)而言,与\(y=x\)这条直线所形成的三角形区域是必选的。类似于这样:
- 所以线段树的做法就很显然了,横坐标直接从后往前枚举,依次加点并且不断在线段树中插入点的信息并更新区间信息,查询的时候就直接查询最大值以及纵坐标即可。
- 为什么是更新区间最大值?
- 因为每插入一个点,它会影响在它右上方的点,当选择右上方的点时,它也必然被包含,类似于这样:(所以直接更新在其上方的区间信息即可)
- 注意一个细节,因为最后还要减去正方形的边长,对于一个点\((x,y)\)而言,正方形边长就为\(y-x\),减去就是加上\(x-y\)。因为我们是按照\(y\)值建立线段树的,初始答案就为\(-y\),最后加上枚举的\(x\)就行了。
最后还要注意特判一下答案为负的情况,可能答案不包含任何点。
详见代码:Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() //#define Local using namespace std; typedef long long ll; typedef pair<ll, ll> pll; const int N = 5e5 + 5; int n; vector <int> v; pll tr[N << 2]; ll add[N << 2]; struct node{ int x, y, c; bool operator < (const node &A) const{ return x < A.x; } }p[N]; void push_up(int o) { tr[o] = max(tr[o << 1], tr[o << 1|1]); } void push_down(int o, int l, int r) { if(add[o]) { tr[o << 1].fi += add[o]; tr[o << 1|1].fi += add[o]; add[o << 1] += add[o]; add[o << 1|1] += add[o]; add[o] = 0; } } void build(int o, int l, int r) { add[o] = 0; if(l == r) { tr[o] = MP(-v[l], v[l]); return; } int mid = (l + r) >> 1; build(o << 1, l, mid); build(o << 1|1, mid + 1, r); push_up(o); } void update(int o, int l, int r, int x, int c) { if(x <= v[l]) { tr[o].fi += c; add[o] += c; return; } push_down(o, l, r); int mid = (l + r) >> 1; if(x <= v[mid]) update(o << 1, l, mid, x, c); update(o << 1|1, mid + 1, r, x, c); push_up(o); } pll query(int o, int l, int r, int L) { if(L > v[r]) return MP(-1000000009, 0); if(L <= v[l]) return tr[o]; push_down(o, l, r); int mid = (l + r) >> 1; pll ret = max(query(o << 1, l, mid, L), query(o << 1|1, mid + 1, r, L)); return ret; } void run() { v.clear(); for(int i = 1; i <= n; i++) { int x, y, c; cin >> x >> y >> c; if(x > y) swap(x, y); p[i] = {x, y, c}; v.push_back(y); } sort(all(v)); v.erase(unique(all(v)), v.end()); build(1, 0, sz(v) - 1); sort(p + 1, p + n + 1); p[0].x = -1; pll ans = MP(-1000000009, 0); int ansx; for(int i = n; i >= 1; i--) { update(1, 0, sz(v) - 1, p[i].y, p[i].c); if(p[i - 1].x != p[i].x) { pll ret = query(1, 0, sz(v) - 1, p[i].x); ret.fi += p[i].x; if(ret > ans) { ans = ret; ansx = p[i].x; } } } if(ans.fi < 0) { ans = MP(0, 1000000001), ansx = 1000000001; } cout << ans.fi << ' ' << ansx << ' ' << ansx << ' ' << ans.se << ' ' << ans.se << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif while(cin >> n) run(); return 0; }