0%

CF997E Good Subsegments

题面描述

询问区间内所有满足重排后连续的区间个数

题解

第一道真正利用线段树历史标记的题

终于知道线段树统计子区间问题的一种方法了

结合 [CERC2017]Intrinsic Interval 食用更佳

考虑把操作离线,枚举每个右端点 \(r\),对于左端点 \(l\) 记录 \(cnt+l-r\)\(cnt\) 为区间 \([l,\ r]\) 的满足 \(|a-b|<1\) 的无序数对 \((a,\ b)\) 的数量,当一个区间合法当且仅当 \(cnt+l-r=0\),其余情况 \(cnt+l-r\le r\),在线段树上维护最大值和最大值出现次数,每次右端点移动时用合法的相邻的数统计一下即可 下面就是如何维护子区间的问题了,我们引入一个新的标记 \(ti\),用于累计历史最大值及其次数的贡献,每次下传时,当且仅当 \(maxv_{fa}\ =\ maxv_{son}\) 是下传 \(ti\),并累计贡献,同时注意 \(add\)\(ti\) 的顺序,应下传 \(add\) 再下传 \(ti\),子节点有还未更新到合法状态

细节见注释

本来以为比单调栈的做法常数小,但其实差的也不多

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
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long int64;
inline int read(int f = 1, int x = 0, char ch = ' ')
{
while(!isdigit(ch = getchar())) if(ch == '-') f = -1;
while(isdigit(ch)) x = x*10+ch-'0', ch = getchar();
return f*x;
}
const int N = 12e4+5;
int64 val[N<<2];
int c[N<<2], v[N<<2], ti[N<<2], a[N<<2];
void pusha(int x, int d) { v[x] += d, a[x] += d; }
void pushti(int x, int d) { val[x] += d*c[x], ti[x] += d; }
void pushdown(int x)
{
if(a[x]) pusha(x<<1, a[x]), pusha(x<<1|1, a[x]), a[x] = 0;
if(ti[x])
{
if(v[x] == v[x<<1]) pushti(x<<1, ti[x]);
if(v[x] == v[x<<1|1]) pushti(x<<1|1, ti[x]);
ti[x] = 0;
}
}
void pushup(int x)
{
v[x] = max(v[x<<1], v[x<<1|1]); c[x] = 0;
if(v[x] == v[x<<1]) c[x] += c[x<<1];
if(v[x] == v[x<<1|1]) c[x] += c[x<<1|1];
val[x] = val[x<<1]+val[x<<1|1];
}
void build(int x, int l, int r)
{
if(l == r) return void(c[x] = 1);
int mid = (l+r)>>1;
build(x<<1, l, mid); build(x<<1|1, mid+1, r);
}
void change(int x, int l, int r, int ql, int qr, int d)
{
if(ql <= l&&r <= qr) return pusha(x, d);
int mid = (l+r)>>1; pushdown(x);
if(ql <= mid) change(x<<1, l, mid, ql, qr, d);
if(qr > mid) change(x<<1|1, mid+1, r, ql, qr, d);
pushup(x);
}
int64 query(int x, int l, int r, int ql, int qr)
{
if(ql <= l&&r <= qr) return val[x];
int mid = (l+r)>>1; int64 ret = 0; pushdown(x);
if(ql <= mid) ret += query(x<<1, l, mid, ql, qr);
if(qr > mid) ret += query(x<<1|1, mid+1, r, ql, qr);
return ret;
}
int n, m, f[N], g[N];
vector<pair<int, int> > s[N];
int64 ans[N];
int main()
{
n = read(); build(1, 1, n);
for(int i = 1; i <= n; ++i) f[i] = read(), g[f[i]] = i;
m = read();
for(int i = 1; i <= m; ++i)
{
int l = read(), r = read();
s[r].push_back(make_pair(l, i));
}
for(int i = 1; i <= n; ++i)
{
pusha(1, -1); change(1, 1, n, i, i, i); // 只能依次加,否则 cnt+l-r <= 0 性质不满足
if(f[i] != 1&&g[f[i]-1] < i) change(1, 1, n, 1, g[f[i]-1], 1);
if(f[i] != n&&g[f[i]+1] < i) change(1, 1, n, 1, g[f[i]+1], 1);
pushti(1, 1);
for(int k = 0; k < s[i].size(); ++k)
ans[s[i][k].second] = query(1, 1, n, s[i][k].first, i);
}
for(int i = 1; i <= m; ++i) printf("%lld\n", ans[i]);
return 0;
}