题目链接:BZOJ3956
题目大意
给出一个序列,若干个询问,问在区间
[l..r]
内,有多少个无序数对(i, j),满足
∀k∈(i,j),val[k]<min(val[i],val[j])
;若
(i,j)=∅
,也视为一对合法点对。
分析
1. 有一个非常简单的暴力算法:对于每个询问,枚举区间内的左右端点,然后扫一遍判定是否合法,复杂度
O(MN3)
。
2.1 上一个算法可以怎么优化?可以先通过
O(N)
的单调栈预处理出的第
i
个点延伸到的最右端点为
2.2 从而
O(1)
判断每个点对是否合法,这样的话就降到了
O(MN2)
。
3.1 经过上面的一个算法,若我们只考虑以第i个点为点对中高度较低的点,那么这样的合法点对至多只有两个:
(i,Right[i])
和
(Left[i],i)
。
3.2 这样的话,我们可以只枚举高度较低点,再稍微去一下重就好了,复杂度
O(MN)
。
4.1 现在,算法的瓶颈在于,是否能不进行区间内的枚举,而得出答案呢。我们把这些点对映射到线段树上:若以第
i
个点为较低点,能有一个合法点对的另一个端点在
4.2 然后主席树求
T[r]−T[l−1]
的区间
[l..r]
的和就好了,时间
O(NlogN)
,空间
O(NlogN)
。
5. 其实能像这样的一步一步的想题还是蛮有趣的。
上代码
#include <stack>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3e5 + 10;
const int M = 3e7 + 10;
const int INF = 0x3f3f3f3f;
int n, m, type;
inline int read() {
char ch;
int ans = 0, neg = 1;
while (!isdigit(ch = getchar()))
if (ch == '-') neg = -1;
while (isdigit(ch))
ans = ans * 10 + ch - '0', ch = getchar();
return ans * neg;
}
int T[N], cnt;
int lc[M], rc[M], tot[M];
int modify(int a, int l, int r, int p, int c) {
int now = ++cnt;
tot[now] = tot[a] + c;
if (l == r) return now;
int mid = (l + r) >> 1;
if (p <= mid) rc[now] = rc[a], lc[now] = modify(lc[a], l, mid, p, c);
else lc[now] = lc[a], rc[now] = modify(rc[a], mid + 1, r, p, c);
return now;
}
int query(int a, int b, int l, int r, int ll, int rr) {
if (l == ll && r == rr) return tot[a] - tot[b];
int mid = (l + r) >> 1;
if (rr <= mid) return query(lc[a], lc[b], l, mid, ll, rr);
else if (ll > mid) return query(rc[a], rc[b], mid + 1, r, ll, rr);
return query(lc[a], lc[b], l, mid, ll, mid) + query(rc[a], rc[b], mid + 1, r, mid + 1, rr);
}
int val[N], lef[N], rig[N];
void calcLef() {
stack <int> S;
for (int i = n; i >= 1; i--) {
while (!S.empty() && val[i] >= val[S.top()])
lef[S.top()] = i, S.pop();
S.push(i);
}
while (!S.empty()) lef[S.top()] = 0, S.pop();
}
void calcRig() {
stack <int> S;
for (int i = 1; i <= n; i++) {
while (!S.empty() && val[i] >= val[S.top()])
rig[S.top()] = i, S.pop();
S.push(i);
}
while (!S.empty()) rig[S.top()] = n + 1, S.pop();
}
void init() {
n = read(), m = read(), type = read();
for (int i = 1; i <= n; i++) val[i] = read();
val[0] = -INF, val[n + 1] = -INF, calcLef(), calcRig();
for (int i = 1; i <= n; i++) {
T[i] = T[i - 1];
if (val[lef[i]] > val[i]) T[i] = modify(T[i], 1, n, lef[i], 1);
if (val[rig[i]] >= val[i]) T[i] = modify(T[i], 1, n, rig[i], 1);
}
}
void figure() {
int last = 0;
for (int i = 1; i <= m; i++) {
int l = read(), r = read();
if (type) l = (l + last - 1) % n + 1, r = (r + last - 1) % n + 1;
if (l > r) swap(l, r);
printf("%d\n", last = query(T[r], T[l - 1], 1, n, l, r));
}
}
int main() {
init();
figure();
return 0;
}
以上