[luogu] 富金森林公园

题目链接:https://www.luogu.org/problemnew/show/P3616

预处理

我们观察这道题,先确定每根石柱的贡献;

每根石柱对答案是否有贡献在于其是否打乱了其左右区间的单调性

大概就是上图的效果,我们的线段树将维护左边那一串数

那么我们如何获得、如何处理这个信息呢?

我们再来看每根石柱;

对于每根石柱来说,如果其对答案有贡献,那么贡献在且只在严格高于它的区间:对于上凸的石柱,它对严格高于它的区间贡献为-1,对于下凹的石柱,它对严格高于它的区间贡献为+1;

那么我们就可以大胆作出如下论断:

严格相邻不相等的石柱群中,对于每根石柱,如果其两侧石柱严格高于它,那么就在h[i]~maxh的区间+1;如果其两侧的石柱严格低于它,那么就在h[i]~maxh的区间-1。最后统计得到的就是在该高度下的山峰总数。

但是我们很快就发现了一个问题,对于下图这种情况,我们统计起来就变得有些困难↓

如果我们统计时要求每根石柱两侧的石柱与这根石柱的高度差都是严格不为0的,那么在这种情况下必然会受到局限。

但是我们很快就通过一种去重的思想找到了解决该问题的方法(其实我想了一晚上才发现的我太弱了QAQ

我们放开严格低于的左边界,放开严格高于判定的右边界,对于这两种情况其单侧石柱不予严格限制;

那么预处理就完成了

接下来我们考虑修改和查询操作:

查询操作就是单点查询数值(这个不用解释吧,每个点数值代表的是水面海拔达到这种高度时的山峰个数)

对于修改操作,其对答案的贡献依然在于打乱了原有石柱的秩序;

我们设某石柱两侧石柱高度较高的为mxw,较低的为miw;

对比如下两张图:

我们发现,某根石柱下降时,至多只会影响两个区间(new,miw]∪(mxw,pre](当然影响不到我们就不考虑了);同样地,某根石柱上升时也至多只会影响这两段区间。我们可以利用线段树的区间修改解决这个问题。

树状数组、zkw线段树维护差分也可以解决这个问题,建议各位尝试去写一下(反正我不写

我写了一个标记永久化的线段树;

上代码↓

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN=1<<18;

int n,m,x,y,z,maxh;
int add[MAXN<<2],h[MAXN];
struct rpg{
    int kd,h,id,to;
    int reh;
}p[MAXN<<1];

bool cmp1(rpg a,rpg b){
    return a.h<b.h;
}bool cmp2(rpg a,rpg b){
    return a.id<b.id;
}

void cadd(int k,int l,int r,int le,int ri,int x){
    if(le<=l&&r<=ri){
        add[k]+=x;
        return;
    }int i=k<<1,mid=l+r>>1;
    if(le<=mid) cadd(i,l,mid,le,ri,x);
    if(mid<ri) cadd(i|1,mid+1,r,le,ri,x);
}

int ask(int k,int l,int r,int p,int x){
    if(l==r) return add[k]+x;
    int i=k<<1,mid=l+r>>1;
    if(p<=mid) return ask(i,l,mid,p,x+add[k]);
    return ask(i|1,mid+1,r,p,x+add[k]);
}

void init(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%d",&x);
        p[i]=(rpg){0,x,i};
    }for(int i=1;i<=m;++i){
        scanf("%d",&x);
        if(x==1){
            scanf("%d",&y);
            p[i+n]=(rpg){1,y,i+n};
        }else{
            scanf("%d%d",&y,&z);
            p[i+n]=(rpg){2,z,i+n,y};
        }
    }return;
}

void reduce(){
    sort(p+1,p+n+m+1,cmp1);p[1].reh=1;
    for(int i=2;i<=n+m;++i){
        if(p[i].h==p[i-1].h) p[i].reh=p[i-1].reh;
        else p[i].reh=p[i-1].reh+1;
    }maxh=p[n+m].reh+1;sort(p+1,p+n+m+1,cmp2);
    for(int i=1;i<=n;++i) h[i]=p[i].reh;
    sort(p+1,p+n+1,cmp1);add[1]=1;
    for(int i=1;i<=n;++i){
        if(h[p[i].id-1]<=p[i].reh&&h[p[i].id+1]<p[i].reh){
            cadd(1,1,maxh,p[i].reh+1,maxh,-1);
        }if(h[p[i].id-1]>p[i].reh&&h[p[i].id+1]>=p[i].reh){
            cadd(1,1,maxh,p[i].reh+1,maxh,1);
        }
    }
    return;
}

void solve(){
    for(int i=n+1;i<=m+n;++i){
        if(p[i].kd==1){
            printf("%d\n",ask(1,1,maxh,p[i].reh,0));
        }else{
            int mxw=max(h[p[i].to-1],h[p[i].to+1]);
            int miw=min(h[p[i].to-1],h[p[i].to+1]);
            if(p[i].reh<h[p[i].to]){
                if(h[p[i].to]>mxw) cadd(1,1,maxh,max(p[i].reh,mxw)+1,h[p[i].to],-1);
                if(p[i].reh<miw) cadd(1,1,maxh,p[i].reh+1,min(miw,h[p[i].to]),1);
            }else if(p[i].reh>h[p[i].to]){
                if(h[p[i].to]<miw) cadd(1,1,maxh,h[p[i].to]+1,min(p[i].reh,miw),-1);
                if(p[i].reh>mxw) cadd(1,1,maxh,max(h[p[i].to],mxw)+1,p[i].reh,1);
            }h[p[i].to]=p[i].reh;
        }
    }return;
}

int main(){
    init();
    reduce();
    solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39766648/article/details/79725888