[JOISC 2021 Day3] 保镖 解题报告
statement
给定
analysis
考虑到人不多,先对所有人的位置进行离散化。我们可以尝试用
注意到所有直线之间的夹角均为
平面直角坐标系中,将
可以从向量的角度证明此结论。
则
由于
那么现在在新矩形中一条边的长度是走过路径长的两倍(在原来的二维平面中由直边转为斜边,这里又翻了
现在每个人可以用一条竖着的线段或横着的线段表示。
考虑保镖只能向上和向右走,那么 dp 可以解决,然而保镖的数量非常大,要换一种考虑方式。
设
- 从自身的起点开始走到自己右上方的一个已经求出
的起点 - 从
走出去
可以预处理出
那么问题转化为如何求一个点右上方最优的点。
对于任意一个起点,我们第一段路只有两种可能,要么先向上再向右,要么先向右再向上。可以发现,答案和点到最优解之间的横/纵距离呈线性关系。
如图,
那么我们可据此将每个点转换成直线,
代码中我还没完全搞明白旋转之后的坐标关系,所以横纵坐标实际上是反过来的,其实这无关紧要,可以看作是关于 mxU
和 mxR
统计的是路径上
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 6005, M = 3e6 + 5;
ll xl[N], xr[N], yl[N], yr[N], c[N], lshx[N], lshy[N];
int n, q, qx[M], qy[M];
ll ans[M], dp[N][N], mxR[N][N], mxU[N][N];
vector<int> inX[N], inY[N];
inline ll read() {
ll x = 0;
int f = 0;
char c = getchar();
for (; !isdigit(c); c = getchar()) f |= c == '-';
for (; isdigit(c); c = getchar()) x = x * 10 + (c & 15);
return f ? -x : x;
}
namespace sgt {
const int L = 15005;
int tot, tag[L], lc[L], rc[L];
ll K[L], B[L];
int ln;
inline void add(ll k, ll b) { ++ln, K[ln] = k, B[ln] = b; }
inline ll y(int id, ll x) { return K[id] * x + B[id]; }
void Change(int& k, ll l, ll r, int id) {
if (!k) k = ++tot;
if (l == r) {
if (!tag[k] || y(tag[k], l) < y(id, l)) tag[k] = id;
return;
}
if (!tag[k]) return tag[k] = id, void();
ll mid = l + r >> 1;
ll y1 = y(tag[k], mid), y2 = y(id, mid);
if (K[tag[k]] > K[id]) {
if (y1 >= y2)
Change(lc[k], l, mid, id);
else {
Change(rc[k], mid + 1, r, tag[k]);
tag[k] = id;
}
} else if (K[tag[k]] < K[id]) {
if (y1 >= y2)
Change(rc[k], mid + 1, r, id);
else {
Change(lc[k], l, mid, tag[k]);
tag[k] = id;
}
} else if (B[tag[k]] < B[id])
tag[k] = id;
}
ll Query(int k, ll l, ll r, ll x) {
if (!k) return -LONG_LONG_MAX;
ll res = y(tag[k], x);
ll mid = l + r >> 1;
if (l == r) return res;
if (x <= mid)
return max(res, Query(lc[k], l, mid, x));
else
return max(res, Query(rc[k], mid + 1, r, x));
}
} // namespace sgt
using namespace sgt;
// inline void chkmax(int& x, const int y) { x = x < y ? y : x; }
inline void chkmax(ll& x, const ll y) { x = x < y ? y : x; }
int main() {
n = read(), q = read();
for (int i = 1; i <= n; ++i) {
ll t = read(), a = read(), b = read();
c[i] = read() / 2;
// before: x = t, y = a; x = t + abs(b - a), y = b;
xl[i] = t + a;
yl[i] = t - a;
xr[i] = t + abs(b - a) + b;
yr[i] = t + abs(b - a) - b;
lshx[i * 2 - 1] = xl[i];
lshx[i * 2] = xr[i];
lshy[i * 2 - 1] = yl[i];
lshy[i * 2] = yr[i];
}
sort(lshx + 1, lshx + n * 2 + 1);
sort(lshy + 1, lshy + n * 2 + 1);
int cntx = unique(lshx + 1, lshx + n * 2 + 1) - lshx - 1;
int cnty = unique(lshy + 1, lshy + n * 2 + 1) - lshy - 1;
for (int i = 1; i <= n; ++i) {
xl[i] = lower_bound(lshx + 1, lshx + cntx + 1, xl[i]) - lshx;
xr[i] = lower_bound(lshx + 1, lshx + cntx + 1, xr[i]) - lshx;
yl[i] = lower_bound(lshy + 1, lshy + cnty + 1, yl[i]) - lshy;
yr[i] = lower_bound(lshy + 1, lshy + cnty + 1, yr[i]) - lshy;
}
for (int i = 1; i <= n; ++i) {
if (xl[i] == xr[i])
for (int j = min(yl[i], yr[i]); j < max(yl[i], yr[i]); ++j)
chkmax(mxU[xl[i]][j], c[i]);
else // yl[i] == yr[i]
for (int j = min(xl[i], xr[i]); j < max(xl[i], xr[i]); ++j)
chkmax(mxR[j][yl[i]], c[i]);
}
for (int i = cntx; i >= 1; --i) {
for (int j = cnty; j >= 1; --j) {
chkmax(dp[i][j], dp[i + 1][j] + 1ll * mxR[i][j] * (lshx[i + 1] - lshx[i]));
chkmax(dp[i][j], dp[i][j + 1] + 1ll * mxU[i][j] * (lshy[j + 1] - lshy[j]));
}
}
for (int i = 1; i <= q; ++i) {
int p = read(), x = read();
qx[i] = p + x, qy[i] = p - x;
inX[lower_bound(lshx + 1, lshx + cntx + 1, qx[i]) - lshx].emplace_back(i);
}
for (int i = 1; i <= cntx; ++i) {
for (int j = 1; j <= cnty; ++j) inY[j].clear();
for (int j : inX[i])
inY[lower_bound(lshy + 1, lshy + cnty + 1, qy[j]) - lshy].emplace_back(j);
memset(lc, 0, sizeof(lc));
memset(rc, 0, sizeof(rc));
memset(tag, 0, sizeof(tag));
tot = ln = 0;
for (int j = cnty; j >= 1; --j) {
add(mxR[i - 1][j], dp[i][j]);
Change(lc[0], 0, 4e9, ln);
for (int v : inY[j]) chkmax(ans[v], Query(1, 0, 4e9, lshx[i] - qx[v]));
}
}
for (int i = 1; i <= cnty; ++i) inY[i].clear();
for (int i = 1; i <= q; ++i)
inY[lower_bound(lshy + 1, lshy + cnty + 1, qy[i]) - lshy].emplace_back(i);
for (int i = 1; i <= cnty; ++i) {
for (int j = 1; j <= cntx; ++j) inX[j].clear();
for (int j : inY[i])
inX[lower_bound(lshx + 1, lshx + cntx + 1, qx[j]) - lshx].emplace_back(j);
memset(lc, 0, sizeof(lc));
memset(rc, 0, sizeof(rc));
memset(tag, 0, sizeof(tag));
tot = ln = 0;
for (int j = cntx; j >= 1; --j) {
add(mxU[j][i - 1], dp[j][i]);
Change(lc[0], 0, 4e9, ln);
for (int v : inX[j]) chkmax(ans[v], Query(1, 0, 4e9, lshy[i] - qy[v]));
}
}
for (int i = 1; i <= q; ++i) printf("%lld\n", ans[i]);
return 0;
}
此题还有一种更精妙的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探