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;
}
posted @ 2023-08-14 14:51  Jijidawang  阅读(8)  评论(0)    收藏  举报  来源