CF1270H Number of Components 题解

Description

给一个长度为 nn 的数组 aaaa 中的元素两两不同。

对于每个数对 (i,j)(i<j)(i,j)(i<j),若 ai<aja_i<a_j,则让 iijj 连一条边。求图中连通块个数。

支持 qq 次修改数组某个位置的值,每次修改后输出图中连通块个数。

n,q5×105,1ai106n,q\le 5\times 10^5,1\le a_i\le 10^6,保证任意时刻数组中元素两两不同。

Solution

考虑怎么求一个序列有几个连通块。

首先有个性质是每个连通块一定是一个区间,证明就考虑钦定 i<k<ji<k<jiijj 连通。如果 ai<aja_i<a_j,则 ai<aka_i<a_kak<aja_k<a_j 必定满足至少一个。

如果 ai>aja_i>a_j,由于 i,ji,j 连通,所以一定存在 x<ix<iai>aj>axa_i>a_j>a_xx>jx>jax>ai>aja_x>a_i>a_j。这样就规约到了 ai<aja_i<a_j 的情况。故结论得证。

所以连通块数就是满足 mini=1kai>maxi=k+1nai\min_{i=1}^{k}{a_i}>\max_{i=k+1}^{n}{a_i}kk 的数量。

考虑枚举前缀的最小值 xx,将 aa 数组中大于等于 xx 的位置设成 11,小于 xx 的位置设成 00,则合法的 xx 一定满足 0101 序列为 1111000011110000 的形式,即 1010 的出现次数为 11

cntxcnt_x 表示 xx0101 序列中 1010 的出现次数。于是题目转化为求对于所有在 aa 数组中出现了的 xxcntx=1cnt_x=1xx 的数量,维护一个值域的线段树即可。

时间复杂度:O((n+q)logn)O\left((n+q)\log n\right)

Code

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
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e6 + 5;

int n, q, m;
int a[kMaxN], pos[kMaxN], x[kMaxN], unq[kMaxN];

struct SGT {
int mi[kMaxN * 4], tag[kMaxN * 4], tot[kMaxN * 4], cnt[kMaxN * 4];

void pushup(int x) {
mi[x] = std::min(tot[x << 1] ? mi[x << 1] : 1e9, tot[x << 1 | 1] ? mi[x << 1 | 1] : 1e9);
tot[x] = tot[x << 1] + tot[x << 1 | 1];
cnt[x] = 0;
if (tot[x << 1] && mi[x] == mi[x << 1]) cnt[x] += cnt[x << 1];
if (tot[x << 1 | 1] && mi[x] == mi[x << 1 | 1]) cnt[x] += cnt[x << 1 | 1];
}

void addtag(int x, int v) {
mi[x] += v, tag[x] += v;
}

void pushdown(int x) {
if (tag[x])
addtag(x << 1, tag[x]), addtag(x << 1 | 1, tag[x]), tag[x] = 0;
}

void build(int x, int l, int r) {
if (l == r) return void(cnt[x] = 1);
int mid = (l + r) >> 1;
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
pushup(x);
}

void update1(int x, int l, int r, int ql, int v) {
if (l == r) return void(tot[x] = v);
pushdown(x);
int mid = (l + r) >> 1;
if (ql <= mid) update1(x << 1, l, mid, ql, v);
else update1(x << 1 | 1, mid + 1, r, ql, v);
pushup(x);
}

void update2(int x, int l, int r, int ql, int qr, int v) {
if (l > qr || r < ql) return;
else if (l >= ql && r <= qr) return addtag(x, v);
pushdown(x);
int mid = (l + r) >> 1;
update2(x << 1, l, mid, ql, qr, v), update2(x << 1 | 1, mid + 1, r, ql, qr, v);
pushup(x);
}

int query() { return tot[1] && mi[1] == 1 ? cnt[1] : 0; }
} sgt;

void discrete() {
for (int i = 0; i <= n + 1; ++i) unq[++m] = a[i];
for (int i = 1; i <= q; ++i) unq[++m] = x[i];
std::sort(unq + 1, unq + 1 + m);
m = std::unique(unq + 1, unq + 1 + m) - unq;

for (int i = 0; i <= n + 1; ++i)
a[i] = std::lower_bound(unq + 1, unq + 1 + m, a[i]) - unq;
for (int i = 1; i <= q; ++i)
x[i] = std::lower_bound(unq + 1, unq + 1 + m, x[i]) - unq;
}

void update(int x, int y, int v) {
if (x > y) sgt.update2(1, 1, m, y + 1, x, v);
}

void dickdreamer() {
std::cin >> n >> q;
a[0] = 1e9, a[n + 1] = -1e9;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
for (int i = 1; i <= q; ++i) std::cin >> pos[i] >> x[i];
discrete();
sgt.build(1, 1, m);
for (int i = 1; i <= n; ++i) sgt.update1(1, 1, m, a[i], 1);
for (int i = 0; i <= n; ++i) update(a[i], a[i + 1], 1);
for (int i = 1; i <= q; ++i) {
update(a[pos[i] - 1], a[pos[i]], -1), update(a[pos[i]], a[pos[i] + 1], -1);
sgt.update1(1, 1, m, a[pos[i]], 0);
a[pos[i]] = x[i];
update(a[pos[i] - 1], a[pos[i]], 1), update(a[pos[i]], a[pos[i] + 1], 1);
sgt.update1(1, 1, m, a[pos[i]], 1);
std::cout << sgt.query() << '\n';
}
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

CF1270H Number of Components 题解
https://sobaliuziao.github.io/2024/09/23/post/52dd282f.html
作者
Egg_laying_master
发布于
2024年9月23日
许可协议