P3616 富金森林公园 题解
连通块数 $=$ 点数 $-$ 边数。
水面海拔为 $x$ 时,点集为海拔 $\ge x$ 的点,所以点数为 $\sum\limits_{i=1}^n[a_i\ge x]$,
两点之间有边,当且仅当两点相邻且两点海拔均 $\ge x$,所以边数为 $\sum\limits_{i=1}^{n-1}[\min(a_i,a_{i+1})\ge x]$,
每次单点修改只会影响一个 $a_i$ 和两个 $\min(a_i,a_{i+1})$,平衡树维护 $a_i$,$\min(a_i,a_{i+1})$ 的集合即可。
#include <cstdio>
#include <utility>
#include <algorithm>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#define P pair<int, int>
using namespace std;
using namespace __gnu_pbds;
int n, m, a[300050];
struct S
{
int c;
tree<P, null_type, less<P>, rb_tree_tag, tree_order_statistics_node_update> T;
void I(int x) { T.insert({x, ++c}); }
void D(int x) { T.erase(T.lower_bound({x, 0})); }
int Q(int x) { return T.order_of_key({x, 0}); }
} c, d;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%d", a + i), d.I(a[i]);
for (int i = 2; i <= n; ++i)
c.I(min(a[i], a[i - 1]));
for (int i = 0, o, x, y; i < m; ++i)
{
scanf("%d%d", &o, &x);
if (o & 1)
printf("%d\n", c.Q(x) - d.Q(x) + 1);
else
{
scanf("%d", &y);
d.D(a[x]);
if (x > 1)
c.D(min(a[x], a[x - 1]));
if (x < n)
c.D(min(a[x], a[x + 1]));
d.I(a[x] = y);
if (x > 1)
c.I(min(a[x], a[x - 1]));
if (x < n)
c.I(min(a[x], a[x + 1]));
}
}
return 0;
}

浙公网安备 33010602011771号