[ABC266Ex] Snuke Panic (2D) 题解
前言
前几天看到了这个,发现自己还真没仔细思考过。
做了两道题,把这个题当个总结。
思路
设 \(f_i\) 表示:在 \(T_i\) 走到第 \(i\) 个节点的最大收益。
有转移:\(f_i=\max \{f_j\} + A_i\)。
其中 \(j\) 需要满足:
- \(y_j\le y_i\)
- \(T_i\le T_i\)
- \(|x_i-x_j|+y_i-y_j\le T_i-T_j\)
第三条限制可以转化为:\(x_i-x_j+y_i-y_j\le T_i-T_j \land x_j-x_i+y_i-y_j\le T_i-T_j\)。
容易发现满足第三条限制一定满足第二条限制,所以有如下限制:
- \(y_i\ge y_j\)
- \(x_i+y_i-T_i\le x_j+y_j-T_j\)
- \(-x_i+y_i-T_i\le -x_j+y_j-T_j\)
然后其实就是三维偏序了。因为这些点没有顺序限制,所以在外层可以排序掉一维,第二维可以用 CDQ 分治处理,第三为用树状数组即可。值得注意的是,因为有初始的点 \((0,0)\),所以我们有 \(n+1\) 个点,离散化后树状数组应该跑到 \(n+1\)。
CDQ 实现的时候为什么和平常写的不太一样?
这里的“平常”指先把左右都递归下去再算贡献。
因为如果我们按平常的做法,其实是左右各自贡献,如下图中橙色。但这会出问题,我们更新最右边的时候用的是没有被左边更新的右端的左侧,所以错了。
粉色的顺序才应该是对的。
代码
#include <bits/stdc++.h>using namespace std;#define int long long#define IL inline
#define vec vector
#define bg begin
#define eb emplace_back
#define emp emplace
#define fi first
#define se second
#define mkp make_pair
#define lb lower_bound
#define ub upper_bound
using ubt = long long;
using uubt = unsigned long long;
using dub = double;
using pii = pair<int, int>;
using puu = pair<ubt, ubt>;IL void ckx(int &x, const int &y) { (x < y) && (x = y); }
IL void ckm(int &x, const int &y) { (x > y) && (x = y); }template <typename T = int>
IL T _R() {T s = 0, w = 1;char c = getchar();while (!isdigit(c)) w = c == '-' ? -1 : 1, c = getchar();while (isdigit(c)) s = s * 10 + c - 48, c = getchar();return s * w;
}const int inf = 1e18;const int N = 1e5;
const int maxN = N + 3;int n;
int f[maxN], id[maxN], fid[maxN];
int sot[maxN];struct node {int x, y, t;int A;int k1, k2;IL void init() {k1 = -x + sot[y] - t;k2 = x + sot[y] - t;}IL friend bool operator < (const node &A, const node &B) {if (A.k2 != B.k2) return A.k2 > B.k2;if (A.k1 != B.k1) return A.k1 > B.k1;if (A.y != B.y) return A.y < B.y;if (A.t != B.t) return A.t < B.t;assert(false);}IL friend bool operator <= (const node &A, const node &B) {return !(B < A);}
} a[maxN];IL bool cmp(int i, int j) { return a[i] < a[j]; }struct BIT {int t[maxN];IL void ins(int x, int b) {for (; x <= n + 1; x += x & -x) ckx(t[x], b);}IL int ask(int x) {int res = -inf;for (; x > 0; x -= x & -x) ckx(res, t[x]);return res;}IL void clr(int x) {for (; x <= n + 1; x += x & -x) t[x] = -inf;}BIT() { memset(t, ~0x3f, sizeof(t)); }
} T;void cdq(int l, int r) {if (l == r) return;int mid = (l + r) >> 1;cdq(l, mid);for (int i = mid + 1; i <= r; i++) fid[i] = id[i];sort(fid + mid + 1, fid + r + 1, cmp);int t1 = l, t2 = mid + 1;while (t1 <= mid && t2 <= r) {if (a[id[t1]] <= a[fid[t2]]) {T.ins(a[id[t1]].y, f[id[t1]]);t1++;} else {ckx(f[fid[t2]], T.ask(a[fid[t2]].y) + a[fid[t2]].A);t2++;}}while (t2 <= r)ckx(f[fid[t2]], T.ask(a[fid[t2]].y) + a[fid[t2]].A),t2++;for (int i = l; i <= mid; i++) T.clr(a[id[i]].y);cdq(mid + 1, r);stable_sort(id + l, id + r + 1, cmp);
}signed main() {n = _R();for (int i = 1; i <= n; i++) {a[i].t = _R(), a[i].x = _R(), a[i].y = _R(), a[i].A = _R();sot[i] = a[i].y;}sot[n + 1] = 0;sort(sot + 1, sot + n + 2);for (int i = 0; i <= n; i++)a[i].y = lb(sot + 1, sot + n + 2, a[i].y) - sot;for (int i = 0; i <= n; i++)a[i].init();sort(a, a + n + 1, [&](const auto &A, const auto &B) {if (A.k1 != B.k1) return A.k1 > B.k1;if (A.k2 != B.k2) return A.k2 > B.k2;if (A.y != B.y) return A.y < B.y;if (A.t != B.t) return A.t < B.t;assert(false);});memset(f, ~0x3f, sizeof(f));f[0] = 0;for (int i = 0; i <= n; i++) id[i] = i;cdq(0, n);int ans = 0;for (int i = 1; i <= n; i++) ckx(ans, f[i]);printf("%lld\n", ans);
}