CF1039E
题意
给 \(n\) 个数 \(x_1,\ x_2,\ ...,\ x_n\) 和 \(w,\ q\)。定义 \(f(l,\ r)\ =\ \min_{l\ \leq\ i\ \leq\ r}\{x_i\}\ -\ \max_{l\ \leq\ i\ \leq\ r}\{x_i\}\)。
\(q\) 次询问,每次给 \(k\) 问最少可以将序列分成几段使得每段的 \(f\) 值都 \(\geq\ k\ -\ w\)。
做法1
直接贪心是对的。考虑暴力,每次对一个点 \(i\) 找能到达的最远点 \(j\),向 \(nxt_i\ =\ j\ +\ 1\) 连边,这样从 \(1\) 到 \(n\ +\ 1\) 的链长就是答案。
如果 \(nxt_i\ -\ i\ >\ B\),则最多跳 \(\frac{n}{B}\) 次,每次可以二分答案。
如果 \(nxt_i\ -\ i\ \leq\ B\),可以将这样的边建成树,由于只会有 \(O(nB)\) 次边的修改,所以用 lct 维护,复杂度 \(O(nB\ log\ n)\)。
取 \(B\ =\ \sqrt{n}\),总复杂度 \(O(n\ \sqrt{n}\ log\ n)\)。
代码
#include <bits/stdc++.h>
#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif
using namespace std;
const int maxn = 1e5 + 10, lgn = 18;
namespace LCT {
struct node {
node *ch[2], *par;
int sz;
node() { memset(ch, 0, sizeof ch); par = 0; sz = 1; }
inline bool isr() const { return !par || par->ch[0] != this && par->ch[1] != this; }
inline int dir() const { return par->ch[1] == this; }
inline void pull() { sz = 1 + (ch[0] ? ch[0]->sz : 0) + (ch[1] ? ch[1]->sz : 0); return; }
inline void setc(int d, node *u) { ch[d] = u; if(u) u->par = this; return; }
} pool[maxn];
inline void rot(node *u) {
node *p = u->par; int d = u->dir();
if(p->isr()) u->par = p->par; else p->par->setc(p->dir(), u);
p->setc(d, u->ch[!d]); u->setc(!d, p);
return p->pull();
}
inline void splay(node *u) {
while(!u->isr()) {
if(!u->par->isr()) rot(u->dir() == u->par->dir() ? u->par : u);
rot(u);
}
return u->pull();
}
inline void access(node *u) {
for (node *v = 0; u; u = u->par) {
splay(u);
u->ch[1] = v;
(v = u)->pull();
}
return;
}
inline void link(int a, int b) { // a -> b
node *u = pool + a, *v = pool + b;
access(u); splay(u);
access(v); splay(v);
u->par = v;
return;
}
inline void cut(int a, int b) { // a -> b
node *u = pool + a, *v = pool + b;
access(v); splay(v);
splay(u);
u->par = 0;
return;
}
inline int dep(int a) {
node *u = pool + a;
access(u); splay(u);
return u->sz;
}
inline int findr(int a) {
node *u = pool + a;
access(u); splay(u);
while(u->ch[0]) u = u->ch[0];
splay(u);
return u - pool;
}
}
int n, w, q, nxt[maxn], ans[maxn], B, stmn[maxn][lgn], stmx[maxn][lgn], LOG[maxn];
set<pair<int, int> > eve;
inline int Qmx(int l, int r) { int lg = LOG[r - l + 1]; return max(stmx[l][lg], stmx[r - (1 << lg) + 1][lg]); }
inline int Qmn(int l, int r) { int lg = LOG[r - l + 1]; return min(stmn[l][lg], stmn[r - (1 << lg) + 1][lg]); }
inline int F(int l, int r) { return Qmn(l, r) - Qmx(l, r); }
int main() {
scanf("%d%d%d", &n, &w, &q);
for (int i = 1; i <= n; ++i) scanf("%d", stmn[i]), stmx[i][0] = stmn[i][0];
for (int lg = 1; lg < lgn; ++lg) {
int l = 1 << lg - 1;
for (int i = 1; i + l <= n; ++i) stmx[i][lg] = max(stmx[i][lg - 1], stmx[i + l][lg - 1]), stmn[i][lg] = min(stmn[i][lg - 1], stmn[i + l][lg - 1]);
}
for (int i = 2; i <= n; ++i) LOG[i] = LOG[i >> 1] + 1;
B = min(min((int)(ceil(sqrt(1.0 * n)) + 2), n), 50);
for (int i = 1; i <= n; ++i) {
nxt[i] = -1;
int j = min(n + 1, i + B);
eve.insert(make_pair(F(i, j - 1) + 1, -i));
}
for (int i = 1; i <= q; ++i) {
int k; scanf("%d", &k);
eve.insert(make_pair(k - w, i));
}
while(eve.size()) {
auto it = eve.begin();
if(it->first > 0) break;
if(it->second > 0) {
int &ans = ::ans[it->second], d = it->first;
ans = -1;
eve.erase(it);
for (int i = 1; i <= n; ) {
if(!~nxt[i]) {
int lb = i, rb = n;
while(lb <= rb) {
int mid = lb + rb >> 1;
if(F(i, mid) < d) rb = mid - 1;
else lb = mid + 1;
}
i = lb;
++ans;
}
else {
ans += LCT::dep(i) - 1;
i = LCT::findr(i);
}
}
}
else {
int i = -it->second, &j = nxt[i], d = it->first;
eve.erase(it);
bool flag = 0;
if(!~j) {
j = i;
while(j + 1 <= n && F(i, j + 1) >= d) ++j;
LCT::link(i, ++j);
}
else {
LCT::cut(i, j);
--j;
while(F(i, j) < d) --j;
LCT::link(i, ++j);
}
if(j > i + 1) eve.insert(make_pair(F(i, j - 1) + 1, -i));
}
}
for (int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
return 0;
}
做法2
仍然考虑如何优化暴力。
如果 \(nxt_i\ -i\ \leq\ A\),则可以暴力维护 \(nxt_i\),并且维护一个 \(i\) 跳多少步能跳到 \(j,\ j\ >\ i\ +\ A\)。共 \(O(nA)\) 次修改,每次修改 \(nxt_k\),只用改 \([k\ -\ A,\ k]\) 的跳步长 \(>\ A\) 的信息。需要数据结构维护每一次 \(k\) 最小的修改。所以此部分时间复杂度为 \(O(nA^2\ +\ nA\ log\ n)\)。
如果 \(i\ +\ A\ \leq\ nxt_i\ \leq\ i\ +\ B\),则可以直接暴力维护 \(nxt_i\),每次询问时直接向后扫看能否更远。若 \(i\ +\ B\ <\ nxt_i\),则暴力倍增。此复杂度为 \(O(nB\ +\ q\frac{n}{B}\ log\ n)\)。
取 \(A\ =\ n^{\frac{1}{3}},\ B\ =\ n^{\frac{2}{3}}\) 时总复杂度 \(O(n^{\frac{5}{3}}\ +\ n^{\frac{4}{3}}\ log\ n)\)。