CF997E

题意

\(n,\ a_1,\ a_2,\ ...,\ a_n\)
\(q\) 次询问,每次给 \(a,\ b\),问有多少个 \(a\ \leq\ l\ \leq\ r\ \leq\ b\) 满足 \(max\{a_l,\ ...,\ a_r\}\ -\ min\{a_l,\ ...,\ a_r\}\ =\ r\ -\ l\)

\(1\ \leq\ n,\ q\ \leq\ 120000,\ a\) 为排列。

做法1

对于一个区间 \([l,\ r]\)\(max\{a_l,\ ...,\ a_r\}\ -\ min\{a_l,\ ...,\ a_r\}\ \geq\ r\ -\ l\) 恒成立。
考虑从左向右扫 \(r\),对每个 \(i\) 维护 \(b_i\ =\ max\{a_i,\ ...,\ a_r\}\ -\ min\{a_i,\ ...,\ a_r\}\ +\ i\ -\ r\)
如果询问为 \(a\ \leq\ l\ \leq\ r\ =\ b\) 的话,直接求当前序列的最小值有多少个即可。可以注意到,\(b_r\ =\ 0,\ b_i\ \geq\ 0\),所以最小值个数就是 \(0\) 的个数。
询问 \(a\ \leq\ l\ \leq\ r\ \leq\ b\) 就需要存储每个位置 \(b\) 的历史上有多少个 \(0\)
注意直接在记录 当前最小值,当前最小值出现次数,历史最小值,历史最小值出现次数 不行,因为这样直接打懒标记到一个区间上时,下传的时间可能经过了类似 \(-1,\ +1\) 这样的操作,而这个操作懒标记合并后其儿子的信息会忽略掉 \(-1\) 的情况,从而导致错误。
观察本题的性质,由于每次需要将当前 \(r\) 状态下的线段树上 \(0\) 的个数计算入每个点的历史最小值个数时 root 的当前最小值必定为 \(0\)。所以我们考虑一种暴力的做法。从线段树根开始递归,如果当前点的当前最小值 \(>\ 0\) 则 return,否则把当前点的当前最小值出现次数计算入当前点的历史最小值出现次数中去。可以发现,这样处理,已经与一个点的历史最小值无关,因为如果一个点被打标记在标记下传之前其和其儿子的当前最小值的相对关系和历史最小值的相对关系是一样的,不会出现之前的 \(-1,\ 1\) 的情况。于是我们对一个节点记录这个节点的当前最小值出现次数要计入历史最小值出现次数中的操作次数 times。
时间复杂度 \(O((n\ +\ q)\ log\ n)\)

代码

#include <bits/stdc++.h>

#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif

using namespace std;

const int maxn = 1e5 + 2e4 + 10;

struct node {
	node *ch[2];
	int mn, cnt, tag, times;
	long long ans;

	node() { memset(ch, 0, sizeof ch); mn = cnt = tag = times = 0; ans = 0; }

	void pull() {
		mn = min(ch[0]->mn, ch[1]->mn);
		ans = ch[0]->ans + ch[1]->ans;
		cnt = (ch[0]->mn == mn ? ch[0]->cnt : 0) + (ch[1]->mn == mn ? ch[1]->cnt : 0);
		return;
	}

	void Mnum(int x) { tag += x; mn += x; return; }
	void Mtimes(int x) { times += x; ans += (long long) cnt * x; return; }

	void push() {
		if(tag) ch[0]->Mnum(tag), ch[1]->Mnum(tag), tag = 0;
		if(times) {
			if(ch[0]->mn == mn) ch[0]->Mtimes(times);
			if(ch[1]->mn == mn) ch[1]->Mtimes(times);
			times = 0;
		}
		return;
	}
} *root;

int n, q, a[maxn];
long long ans[maxn];
vector<pair<int, int> > all[maxn], mn, mx;

void B(node* &u = root, int l = 1, int r = n) {
	u = new node;
	if(l == r) { u->mn = l; u->cnt = 1; return; }
	int mid = l + r >> 1;
	B(u->ch[0], l, mid);
	B(u->ch[1], mid + 1, r);
	return u->pull();
}

void M(int a, int b, int x, node* &u = root, int l = 1, int r = n) {
	if(a <= l && r <= b) { u->Mnum(x); return; }
	int mid = l + r >> 1; u->push();
	if(a <= mid) M(a, b, x, u->ch[0], l, mid);
	if(mid < b) M(a, b, x, u->ch[1], mid + 1, r);
	return u->pull();
}

long long Q(int a, int b, node* &u = root, int l = 1, int r = n) {
	if(a <= l && r <= b) return u->ans;
	int mid = l + r >> 1; long long ans = 0; u->push();
	if(a <= mid) ans += Q(a, b, u->ch[0], l, mid);
	if(mid < b) ans += Q(a, b, u->ch[1], mid + 1, r);
	return u->pull(), ans;
}

int main() {
	scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%d", a + i);
	scanf("%d", &q); for (int l, r, i = 1; i <= q; ++i) scanf("%d%d", &l, &r), all[r].push_back(make_pair(l, i));
	B();
	for (int lst, i = 1; i <= n; ++i) {
		M(1, n, -1);
		lst = i;
		while(mx.size() && mx.back().first <= a[i]) {
			M(mx.back().second, lst - 1, a[i] - mx.back().first);
			lst = mx.back().second;
			mx.pop_back();
		}
		mx.push_back(make_pair(a[i], lst));
		lst = i;
		while(mn.size() && mn.back().first >= a[i]) {
			M(mn.back().second, lst - 1, mn.back().first - a[i]);
			lst = mn.back().second;
			mn.pop_back();
		}
		mn.push_back(make_pair(a[i], lst));
		root->Mtimes(1);
		for (auto &t: all[i]) ans[t.second] = Q(t.first, i);
	}
	for (int i = 1; i <= q; ++i) printf("%"LLFORMAT"d\n", ans[i]);
	return 0;
}
posted @ 2018-10-02 19:15  King_George  阅读(563)  评论(0编辑  收藏  举报