https://ac.nowcoder.com/acm/contest/6112

E - 骚区间

 

题意:定义一个区间是骚的,当且仅当区间左端点是次小值,右端点是次大值。给定一个排列,问有几个骚区间。

权值线段树+树状数组

虽然这里线段树完全没必要。。

假设已经知道了每个点右边最近的两个比它小的数,以及左边最近的比它大的数。则可以一遍遍历左端点,一遍更新右端点同时统计可行区间。

具体方法为遍历每一位作为区间左端点,并且找到满足当前位置在右端点的两个左边比它大的数之间的右端点,这可以通过树状数组求得两个前缀的差得到一个区间的右端点个数。在移动到下一个左端点前更新满足条件的右端点。

再考虑求每个点右边最近的两个比它小的数,以及左边最近的比它大的数。这可以通过一边遍历一边插入权值线段树实现,并改变一次值来获得第二近的位置。

但是实际上可以用set实现,通过遍历权值,可以保证已经存在set里的位置上都是小于当前的数,只要upperbound找最近的大于的位置即可,再指针++就是第二近的位置。找左边较小的可以存负值类似实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int N = 1e6 + 10;
int n;
int a[N], p[N];
#define mid ((l+r)>>1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
int mn[N << 2], mx[N << 2];
void up(int rt) {
mn[rt] = min(mn[rt << 1], mn[rt << 1 | 1]);
mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]);
}
void cgmn(int q, int x, int l, int r, int rt) {
if (l == r) {
mn[rt] = x;
return;
}
if (q <= mid)cgmn(q, x, lson);
else cgmn(q, x, rson);
up(rt);
}
void cgmx(int q, int x, int l, int r, int rt) {
if (l == r) {
mx[rt] = x;
return;
}
if (q <= mid)cgmx(q, x, lson);
else cgmx(q, x, rson);
up(rt);
}
int qrymn(int ql, int qr, int l, int r, int rt) {
if (ql <= l && qr >= r)return mn[rt];
int ans = INF;
if (ql <= mid)ans = min(ans, qrymn(ql, qr, lson));
if (qr > mid)ans = min(ans, qrymn(ql, qr, rson));
return ans;
}
int qrymx(int ql, int qr, int l, int r, int rt) {
if (ql <= l && qr >= r)return mx[rt];
int ans = 0;
if (ql <= mid)ans = max(ans, qrymx(ql, qr, lson));
if (qr > mid)ans = max(ans, qrymx(ql, qr, rson));
return ans;
}
int l[N][2], r[N][2];
ll ans;
int sum[N];
int lowbit(int x) { return x & -x; }
void add(int p, int x) {
for (int i = p; i <= n + 1; i += lowbit(i)) {
sum[i] += x;
}
}
int qry(int r) {
int res = 0;
for (int i = r; i > 0; i -= lowbit(i)) {
res += sum[i];
}
return res;
}
vector<int>vc[N][2];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]), p[a[i]] = i;
fill(mn, mn + 4 * n, n + 1);
for (int i = 1; i <= n; i++) {
l[i][0] = qrymx(a[i], n, 1, n, 1);
int t = l[i][0];
if (t == 0) {
l[i][1] = 0;
cgmx(a[i], i, 1, n, 1);
continue;
}
vc[t][0].push_back(i);
cgmx(a[t], 0, 1, n, 1);
l[i][1] = qrymx(a[i], n, 1, n, 1);
vc[l[i][1]][1].push_back(i);
cgmx(a[t], t, 1, n, 1);
cgmx(a[i], i, 1, n, 1);
}
for (int i = n; i >= 1; i--) {
r[i][0] = qrymn(1, a[i], 1, n, 1);
int t = r[i][0];
if (t == n + 1) {
r[i][1] = t;
cgmn(a[i], i, 1, n, 1);
continue;
}
cgmn(a[t], n + 1, 1, n, 1);
r[i][1] = qrymn(1, a[i], 1, n, 1);
cgmn(a[t], t, 1, n, 1);
cgmn(a[i], i, 1, n, 1);
}
for (int i = 1; i <= n; i++) {
if (l[i][1] == 0 && l[i][0] != 0)
add(i, 1);
}
for (int i = 1; i <= n; i++) {
ans += qry(r[i][1] - 1) - qry(r[i][0] - 1);
for (int u : vc[i][1])add(u, 1);
for (int u : vc[i][0])add(u, -1);
}
printf("%lld\n", ans);
return 0;
}