题意

\(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)\)

版权声明:本文为King-George原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/King-George/p/9734863.html