Educational Codeforces Round 73 (Rated for Div. 2)

天大地大妈咪最大 提交于 2019-11-30 04:23:06

传送门

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; }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!