富金森林公园

发布时间 2023-08-15 07:42:02作者: Melting_Pot

很有意思的一道题,来水一篇:

  • Analysis:

    首先,我们对于每一个连通块,都需要找到一个特征值来统计,思考一下就知道它是每个连通块的左或右端点,本文使用左端点来统计。

    进而,我们考虑对于每一种高度的答案分开统计,我们用 \(0\) 代表在当前高度下露出水面的石柱,用 \(1\) 表示当前高度下被水淹没的石柱,然后我们思考以下情况:

    约定:\(H_i\) 轴代表高度,\(X_i\) 轴表示石柱:

    \[\begin{matrix} H_6(0)&&0&0&0&0&0\\ H_5(1)&&0&0&1&0&0\\ H_4(1)&&0&0&1&1&1\\ H_3(2)&&1&0&1&1&1\\ H_2(1)&&1&1&1&1&1\\ H_1(1)&&1&1&1&1&1\\\\ &&X_1&X_2&X_3&X_4&X_5 \end{matrix}\]

    我们考虑修改操作:对于 \(X_i\) ,它的降低显然会对降低后对应的答案造成贡献,具体来说:当石柱 \(X_i\) 降低时,我们分别考虑所有会变化的值(答案),如果当前 \(H\) 下,减少的那个石柱的左右两边为空,则当前高度下连通块的数量减少 \(1\),否则就增加 \(1\),我们不妨手玩一下:

    假设当前 \(X_3\) 减少到了 \(H_1\) 那么原图转化为:

    \[\begin{matrix} H_6(0)&&0&0&0&0&0\\ H_5(1-1)&&0&0&0&0&0\\ H_4(1)&&0&0&0&1&1\\ H_3(2)&&1&0&0&1&1\\ H_2(1+1)&&1&1&0&1&1\\ H_1(1)&&1&1&1&1&1\\\\ &&X_1&X_2&X_3&X_4&X_5 \end{matrix}\]

    我们发现:二维点对 \((X_3,H_5)\) 两边均无石柱,因此 \(H_5\) 中的贡献减一为零,而 \((X_3,H_2)\) 的两边均有石柱,因此 \(H_2\) 中的贡献加一为二。\((X_3,H_3)\)\((X_3,H_4)\) 两边一边有石柱,一边没有,那么修改就对当前答案无贡献,因此 \(H_3\)\(H_4\) 不变。

    到此我们已经找到了规律,我们很容易将其推广到石柱上升时的情况,那么对于以上修改时的三种情况:两边均有,两边均无,一边有一边无。我们就可以分讨实现修改了。

  • Achieve:

    数据结构维护一下我们上文提到的 \(H_i\),既然是高度,当然要离散化。然后,根据上面的手模,每次的答案更新不同,很麻烦,如何避免?我们考虑每次在修改前,将本次修改影响的答案区间贡献减一,再正常操作,不明白?我们再来手玩一下:

    还是上面我们提到的情况:将 \(X_3\) 降低至 \(H_1\),因为修改之前 \(X_2\)的高度 小于 \(X_3\),所以我们先将 \(H_{2+1}\)\(H_3\)\(H_5\) 的区间减一消除影响,再将 \(X_3\) 修改至 \(H_1\) 高度,此时 \(X_3\) 的高度小于 \(X_4\),能产生新的左端点,因此 \(H_{1+1}\)\(H_2\)\(H_4\) 答案区间加一。至此,\(H_3\)\(H_4\) 的影响抵消,我们完成了想要实现的修改操作。

    至于为什么区间修改时,要将左高度加一,这是因为本文选用左端点统计答案,做高度本身就能成为一个左端点。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=400005;
struct node{
    int h,id;
    bool operator <(const node &b)const
    {return h<b.h;}
}a[N<<1];
int h[N<<1],x[N<<1],k[N<<1];
int n,m,mx;
namespace TREE{
    int dat[N<<2],tag[N<<2];
    void pushdown(int pos){
        if(!tag[pos]) return;
        int lson=pos<<1,rson=pos<<1|1;
        dat[lson]+=tag[pos],dat[rson]+=tag[pos];
        tag[lson]+=tag[pos],tag[rson]+=tag[pos];
        tag[pos]=0;
    }
    void modify(int pos,int l,int r,int ql,int qr,int x){
        if(ql<=l&&qr>=r){
            dat[pos]+=x,tag[pos]+=x;return;
        }
        pushdown(pos);
        int mid=(l+r)>>1;
        if(ql<=mid) modify(pos<<1,l,mid,ql,qr,x);
        if(qr>mid) modify(pos<<1|1,mid+1,r,ql,qr,x);
    }
    int query(int pos,int l,int r,int x){
        if(l==r) return dat[pos];
        pushdown(pos);
        int mid=(l+r)>>1;
        if(x<=mid) return query(pos<<1,l,mid,x);
        else return query(pos<<1|1,mid+1,r,x);
    }

}
int main(){
    freopen("darkteam.in","r",stdin);
    freopen("darkteam.out","w",stdout);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;++i)
        scanf("%d",&a[i].h),a[i].id=i;
    for(int i=n+1;i<=n+m;++i){
        scanf("%d",&k[i]);
        if(k[i]==2) scanf("%d",&x[i]);
        scanf("%d",&a[i].h);
        a[i].id=i;
    }
    sort(a+1,a+1+n+m);
    h[a[1].id]=1;
    for(int i=2;i<=n+m;++i) h[a[i].id]=a[i].h>a[i-1].h?h[a[i-1].id]+1:h[a[i-1].id];
    mx=h[a[n+m].id];
    for(int i=1;i<=n;++i) if(h[i-1]<h[i]) TREE::modify(1,1,mx,h[i-1]+1,h[i],1);
    for(int i=n+1;i<=n+m;++i){
        if(k[i]==2){
            int t=x[i];
            if(h[t-1]<h[t]) TREE::modify(1,1,mx,h[t-1]+1,h[t],-1);
            if(t!=n&&h[t]<h[t+1]) TREE::modify(1,1,mx,h[t]+1,h[t+1],-1);
            h[t]=h[i];
            if(h[t-1]<h[t]) TREE::modify(1,1,mx,h[t-1]+1,h[t],1);
            if(t!=n&&h[t]<h[t+1]) TREE::modify(1,1,mx,h[t]+1,h[t+1],1);
        }
        else printf("%d\n",TREE::query(1,1,mx,h[i]));
    }
    return 0;
}