CF1270H Number of Components
我们发现假若 \(l,r\) 有边,则 \([l,r]\) 之间所有的都在这个块内。归纳易得:最终每个连通块必定是一个区间。
考虑重新刻画答案的计数方式。我们尝试在连通块间的的断点处计数,此时存在一个 \(v\),使得连通块左边 \(\geq v\) 且 \(\min=v\) ,右边 \(< v\)(注意到断点和 \(v\) 构成双射)。发现断点处较难以维护计数。
令 \(a_{n+1}=0\),此时画出一张折线图(每个点 \(i\) 对应这坐标 \((i,a_i)\)),会发现合法的 \(v\) 都形如:左边存在最低点 \(=v\),断点处的折线恰好经过 \(v\) 一次,右边都 \(<v\) 且与 \(v\) 不太相关。
尝试对这样形态下的 \(v\) 进行计数。我们将所有的 \((a_i,a_{i+1}]\) 区间中的数全部 \(+1\)。发现此时除去最低点以外,所有合法的 \(v\) 全部出现了 \(1\) 次。
每次修改会产生 \(O(1)\) 对 \((a_i,a_{i+1}]\) 的改变,可以直接用线段树维护最小出现次数。
- 注:所以上面令 \(a_{n+1}=0\) 的用意实际上是为了——让 \(a\) 中最小的那个数也和其它要统计的 \(v\) 一样被覆盖 \(1\) 次。容易发现这样并不影响其它 \(v\) 计数,
点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for (int i = (a); i <= (b); ++i)
#define FR(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
const int N = 5e5 + 10;
const int M = 1e6 + 10;
const int V = 1e6, INF = 1e9;
int n, q, a[N], vis[M];
struct SGT {
struct Node {
int mn, cnt, lazy;
void tag(int v) {
mn += v, lazy += v;
}
friend Node operator + (Node a, Node b) {
Node c = {min(a.mn, b.mn)};
if (a.mn == c.mn)
c.cnt += a.cnt;
if (b.mn == c.mn)
c.cnt += b.cnt;
return c;
}
} t[M << 2];
void PushUp(int p) {
t[p] = t[p << 1] + t[p << 1 | 1];
}
void PushDown(int p) {
if (!t[p].lazy) return;
t[p << 1].tag(t[p].lazy);
t[p << 1 | 1].tag(t[p].lazy);
t[p].lazy = 0;
}
void Build(int p, int l, int r) {
if (l == r) {
if (vis[l]) {
t[p] = Node{0, 1};
} else {
t[p] = Node{INF, 0};
}
return;
}
int mid = (l + r) >> 1;
Build(p << 1, l, mid);
Build(p << 1 | 1, mid + 1, r);
PushUp(p);
}
void Add(int p, int l, int r, int L, int R, int v) {
if (L <= l && r <= R)
return t[p].tag(v);
PushDown(p);
int mid = (l + r) >> 1;
if (L <= mid)
Add(p << 1, l, mid, L, R, v);
if (mid < R)
Add(p << 1 | 1, mid + 1, r, L, R, v);
PushUp(p);
}
void Upd(int p, int l, int r, int x, int v) {
if (l == r) {
t[p].mn -= v * INF;
t[p].cnt += v;
return;
}
PushDown(p);
int mid = (l + r) >> 1;
if (x <= mid)
Upd(p << 1, l, mid, x, v);
if (mid < x)
Upd(p << 1 | 1, mid + 1, r, x, v);
PushUp(p);
}
} sgt;
void Chg(int x, int v) {
if (x < 1 || x > n) return;
int L = min(a[x], a[x + 1]) + 1;
int R = max(a[x], a[x + 1]);
sgt.Add(1, 1, V, L, R, v);
}
int main() {
scanf("%d %d", &n, &q);
FL(i, 1, n) {
scanf("%d", &a[i]);
vis[a[i]] = 1;
}
sgt.Build(1, 1, V);
FL(i, 1, n) Chg(i, 1);
FL(i, 1, q) {
int x, v;
scanf("%d %d", &x, &v);
{
Chg(x - 1, -1);
Chg(x, -1);
sgt.Upd(1, 1, V, a[x], -1);
}
a[x] = v;
{
Chg(x - 1, 1);
Chg(x, 1);
sgt.Upd(1, 1, V, a[x], 1);
}
printf("%d\n", sgt.t[1].cnt);
}
return 0;
}