luogu 3246 莫队+RMQ+单调栈

hnoi 2016

标签:题解


莫队

考虑左端点左移以及右端点右移产生的贡献
这样就可以由 \([l, r]\) 转移到另外的 \(4\) 个区间
\(f_{l, r}\) 表示右端点在 \(r\), 左端点在 \([l, r]\) 内的答案,这个可以压掉一维
\(pre_{i}\) 表示 \(i\) 前面第一个比 \(a_{i}\) 小的数的位置
\(bhd_{i}\) 表示 \(i\) 后面第一个比 \(a_{i}\) 小的数的位置

考虑计算贡献 \(S\)
也就是右端点右移产生的贡献
显然新增加贡献的区间的右端点只有 \(r + 1\)
发现,在 \((pre_{r} + 1, r + 1]\) 这段区间是单调递减的
那么所有左端点在 \((pre_{r} + 1, r + 1]\) 产生的贡献为 \(a_{r + 1} * Len, Len\) 表示区间长度
我们现在另 \(r2 = pre_{r}\), 那么所有左端点在 \((pre_{r2} + 1, r2]\) 产生的贡献为 \(a_{r2} * Len\)
一直这样下去的话就会找到一个 \(x\) 使得 \(a_{pre_{x}}\) 为区间 \([l, r + 1]\) 的最小值, 记 \(p = pre_{x}\)
记此时的总和为 \(S_{1}\) 就是之前的区间产生的贡献
并且所有左端点在 \([l, p]\) 产生的贡献为 \(S_{2} = a_{p} \times ({p - l + 1})\)
那么

\[f_{l, r + 1} = a_{r + 1} \times (r + 1 - pre_{r + 1}) + \cdots + a_{x} \times (x - pre_{x}) + f_{l, p} \]

压掉第一维

\[f_{r + 1} = a_{r + 1} \times (r + 1 - pre_{r + 1}) + \cdots + a_{x} \times (x - pre_{x}) + f_{p} \]

增量

\[S_{2} = f_{r + 1} - f_{p} \]

对于 \(f_{i}\) 预处理, \(O(1)\) 查询增量

\[S = S_{1} + S_{2} \]

对于左端点左移
对称处理 \(f_{}\)

#include <bits/stdc++.h>

const int N = 1e5 + 10;

int A[N], pos[N], size;
int n, m;
struct Node {
	int l, r, id;
	bool operator < (Node a) const {
		if(pos[l] == pos[a.l]) return r < a.r;
		return l < a.l;
	}
} Ask[N];
int Log[N], Pow[N];
int f[N][30];
int Stack[N], topp, pre[N], bhd[N];
int block;

#define LL long long
LL fl[N], fr[N], Answer[N];

inline int Ask_min(int l, int r) {
	int t = Log[r - l + 1];
	return (A[f[l][t]] < A[f[r - Pow[t] + 1][t]] ? f[l][t] : f[r - Pow[t] + 1][t]);
}

inline LL Left(int l, int r) {
	int p = Ask_min(l - 1, r);
	return 1ll * A[p] * (r - p + 1) + fl[l - 1] - fl[p];
}

inline LL Right(int l, int r) {
	int p = Ask_min(l, r + 1);
	return 1ll * A[p] * (p - l + 1) + fr[r + 1] - fr[p];
}

#define Rep(i, a, n) for(int i = a; i <= n; i ++)

int main() {
	std:: cin >> n >> m;
	for(int i = 1; i <= n; i ++) std:: cin >> A[i];
	A[0] = A[n + 1] = (1 << 30);
	for(int i = 1; i <= m; i ++) {
		std:: cin >> Ask[i].l >> Ask[i].r;
		Ask[i].id = i;
	}
	block = sqrt(n);
	for(int i = 1; i <= n; i ++) pos[i] = (i - 1) / block + 1;
	for(int i = 1; i <= n; i ++) f[i][0] = i;
	std:: sort(Ask + 1, Ask + m + 1);
	for(int i = 0; (Pow[i] = (1 << i)) <= n; i ++);
	for(int i = 2; i <= n; i ++) Log[i] = Log[i >> 1] + 1;
	for(int i = 1; Pow[i] <= n; i ++)
		for(int j = 1; j <= n - Pow[i - 1] + 1; j ++)
			f[j][i] = (A[f[j][i - 1]] > A[f[j + Pow[i - 1]][i - 1]] ? f[j + Pow[i - 1]][i - 1] : f[j][i - 1]);
	for(int i = 1; i <= n; i ++) {
		while(topp && A[Stack[topp]] > A[i]) {
			bhd[Stack[topp]] = i;
			topp --;
		}
		pre[i] = Stack[topp];
		Stack[++ topp] = i;
	}
	while(topp) bhd[Stack[topp --]] = n + 1;
	for(int i = 1; i <= n; i ++) fr[i] = fr[pre[i]] + 1ll * A[i] * (i - pre[i]);
	for(int i = n; i >= 1; i --) fl[i] = fl[bhd[i]] + 1ll * A[i] * (bhd[i] - i);
	int l = 1, r = 0;
	LL Ans = 0;
	for(int i = 1; i <= m; i ++) {
		int x = Ask[i].l, y = Ask[i].r;
		while(l > x) Ans += Left(l, r), l --;
		while(r < y) Ans += Right(l, r), r ++;
		while(l < x) Ans -= Left(l + 1, r), l ++;
		while(r > y) Ans -= Right(l, r - 1), r --;
		Answer[Ask[i].id] = Ans;
	}
	for(int i = 1; i <= m; i ++) std:: cout << Answer[i] << "\n";
	return 0;
}

posted @ 2018-09-20 09:34  xayata  阅读(139)  评论(1编辑  收藏  举报