51nod 1463 找朋友
题意
给定两个长度为 \(N\) 的数列 \(A,B\),一个大小为 \(M\) 的集合 \(K\)
有 \(Q\) 次询问,每次询问 \([l,r]\),输出区间内满足 \(|B_i-B_j|\in K\) 的最大 \(A_i+A_j\)
满足 \(N,Q \leq 10^5, M\leq 10\) 并且 \(B\) 是一个 \(1 \to N\) 的排列
解法
虽然很巧,但是好像这类题的套路都很相似。。
发现 \(M\) 很小,考虑暴力枚举 \(K\) 中的元素 \(K_i\)
那么询问 \([l,r]\) 对于我们枚举的 \(K_i\) 就转化为了输出满足 \(|B_i-B_j|=K\) 的最大 \(A_i+A_j\)
我们把询问离线下来按照右端点排序
每次新加入一个 \(B_i\),就在它之前寻找满足 \(|B_i-B_j|=K\) 的 \(j\) 位置,并把 \(A_i+A_j\) 这一贡献加入 \(j\) 位置(取最大值转移)
可以发现对于一个 \(B_i\),满足条件的 \(B_j\) 最多只有两个,又 \(B\) 是一个排列,所以完全可以映射值与位置找到 \(j\) 位置
那么对于一个右端点为 \(R\) 的询问,我们用线段树维护一下 \([L,R]\) 中的最大值即可,之前的处理方式可以保证线段树中 \([L,R]\) 存储的值一定是在 \([L,R]\) 之间的 \(A_i+A_j\) 点对
代码
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
using namespace std;
#define rep(i, a, b) for (int i = a; i <= b; ++i)
#define per(i, a, b) for (int i = a; i >= b; --i)
const int MAX_N = 5e5 + 10;
int read();
struct qry { int l, id; };
int N, Q, M;
int a[MAX_N], b[MAX_N], k[20];
int pos[MAX_N], ans[MAX_N];
vector<qry> q[MAX_N];
inline void chkmax(int& x, int v) { x = x > v ? x : v; }
struct SegTree {
#define ls(x) x << 1
#define rs(x) x << 1 | 1
int val[MAX_N << 2];
void clear() { memset(val, 0, sizeof val); }
void pushup(int x) { val[x] = max(val[ls(x)], val[rs(x)]); }
void modify(int x, int l, int r, int k, int v) {
if (l == r) return chkmax(val[x], v), void();
int mid = (l + r) >> 1;
if (k <= mid)
modify(ls(x), l, mid, k, v);
else
modify(rs(x), mid + 1, r, k, v);
pushup(x);
}
int query(int x, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return val[x];
int mid = (l + r) >> 1, res = 0;
if (ql <= mid)
chkmax(res, query(ls(x), l, mid, ql, qr));
if (qr > mid)
chkmax(res, query(rs(x), mid + 1, r, ql, qr));
return res;
}
#undef ls
#undef rs
} tr;
int main() {
N = read(), Q = read(), M = read();
rep (i, 1, N) a[i] = read();
rep (i, 1, N) b[i] = read(), pos[b[i]] = i;
rep (i, 1, M) k[i] = read();
rep (i, 1, Q) {
int l = read(), r = read();
q[r].push_back((qry){l, i});
}
rep (i, 1, M) {
int K = k[i];
tr.clear();
rep (j, 1, N) {
if (b[j] > K && pos[b[j] - K] <= j)
tr.modify(1, 1, N, pos[b[j] - K], a[j] + a[pos[b[j] - K]]);
if (b[j] + K <= N && pos[b[j] + K] <= j)
tr.modify(1, 1, N, pos[b[j] + K], a[j] + a[pos[b[j] + K]]);
for (int p = 0, s = q[j].size(); p < s; ++p)
chkmax(ans[q[j][p].id], tr.query(1, 1, N, q[j][p].l, j));
}
}
rep (i, 1, Q) printf("%d\n", ans[i]);
return 0;
}
int read() {
int x = 0, c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
return x;
}