其实这题很套路。像HNOI2017影魔的模型,大概可以概括为:
给定你一个序列,在不修改的前提下,求一个区间满足某种条件的二元组的个数/最值/和,条件一般和最值有关, 这就方便我们进行维护。
解决此类问题的一般思路是,首先离线,然后在固定某个端点的情况下处理另一个端点。处理容易发现,另一个端点的值往往是满足某些类单调的性质。这就可以严格限制另一个端点扫到的元素个数,从而保证复杂度。 这里一般开一个线段树进行维护。
在本题中,有端点必须满足\[ j‘ - j < j - i \], 从而推出j递减的个数为\(O(logn)\)次,可以采用主席树进行维护,进而用线段树维护历史二元组的值。
复杂度似乎是 \(O(n\sqrt n)\) 的莫队,链接
详细题解:
转载自:这里
将所有询问离线,按右端点排序。
枚举右端点r,用线段树维护对于当前的右端点,每个左端点对应的答案。
线段树每个结点维护对应区间归并排序后的状态,即构造一棵归并树。
插入新的右端点就相当于在线段树对应区间找到最接近的数,然后更新答案。
显然暴力更新的复杂度在最坏情况下是单次\(O(n)\)的。
发现由于右端点固定,若左边的答案已经不会比右边优,则左端点已经没有更新的必要。先更新右子区间,再更新左子区间剪枝即可。
时间复杂度\(O(mlog^2n)\)
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
int read() {
char ch = getchar();
int x = 0, flag = 1;
for (;!isdigit(ch); ch = getchar()) if (ch == ‘-‘) flag *= -1;
for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
return x * flag;
}
void write(int x) {
if (x < 0) putchar(‘-‘), x = -x;
if (x >= 10) write(x / 10);
putchar(x % 10 + 48);
}
const int Maxn = 2e5 + 9, Maxm = 4e5 + 9, INF = 0x3f3f3f3f;
int n, m, a[Maxn];
vector <pair<int, int> > ls[Maxn];
int ans[Maxm];
struct BIT {
int c[Maxn];
#define lowbit(x) ((x) & -(x))
void modify(int pos, int val) {
for (; pos; pos -= lowbit(pos)) c[pos] = min(c[pos], val);
}
int query(int pos) {
int res = 0x3f3f3f3f;
for (; pos < Maxn; pos += lowbit(pos)) res = min(res, c[pos]);
return res;
}
#undef lowbit
}s;
int rt[Maxn];
struct PresidentTree {
int ls[Maxn * 32], rs[Maxn * 32], Max[Maxn * 32], cnt;
void init() {
clar(ls, 0); clar(rs, 0); clar(Max, 0); cnt = 0;
}
int modify(int pre, int l, int r, int pos, int val) {
int o = ++cnt;
ls[o] = ls[pre]; rs[o] = rs[pre]; Max[o] = max(Max[o], val);
if (l == r) return o;
int mid = (l + r) >> 1;
(pos <= mid) ? ls[o] = modify(ls[pre], l, mid, pos, val) : rs[o] = modify(rs[pre], mid + 1, r, pos, val);
return o;
}
int query(int rt, int l, int r, int p, int q) {
if (p > q || !rt) return 0;
if (p <= l && r <= q) return Max[rt];
int mid = (l + r) >> 1;
if (q <= mid) return query(ls[rt], l, mid, p, q);
else if (p >= mid + 1) return query(rs[rt], mid + 1, r, p, q);
else return max(query(rs[rt], mid + 1, r, p, q), query(ls[rt], l, mid, p, q));
}
}t;
void init() {
clar(ans, 0x3f);
n = read();
rep (i, 1, n) a[i] = read();
m = read();
rep (i, 1, m) {
int l = read(), r = read();
ls[r].push_back(make_pair(l, i));
}
}
void calc() {
t.init(); clar(s.c, 0x3f);
rep (i, 1, n) rt[i] = t.modify(rt[i - 1], 1, 0x3f3f3f3f, a[i], i);
rep (i, 1, n) {
int cur = t.query(rt[i - 1], 1, 0x3f3f3f3f, a[i], 0x3f3f3f3f);
while (cur != 0) {
s.modify(cur, a[cur] - a[i]);
cur = t.query(rt[cur - 1], 1, 0x3f3f3f3f, a[i], (a[i] + a[cur]) / 2 - !((a[i] + a[cur]) & 1));
}
rep (j, 0, ls[i].size() - 1) ans[ls[i][j].second] = min(ans[ls[i][j].second], s.query(ls[i][j].first));
}
}
void solve() {
calc();
rep (i, 1, n) a[i] = 0x3f3f3f3f - a[i];
calc();
rep (i, 1, m) printf("%d\n", ans[i]);
}
int main() {
freopen("bridge.in", "r", stdin);
freopen("bridge.out", "w", stdout);
init();
solve();
#ifdef Qrsikno
debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
return 0;
}
原文:https://www.cnblogs.com/qrsikno/p/10128898.html