洛谷[P3616] 富金森林公园【数据结构】【线段树】【树状数组】

Description

博艾的富金森林公园里有一个长长的富金山脉,山脉是由一块块巨石并列构成的,编号从1到N。每一个巨石有一个海拔高度。而这个山脉又在一个盆地中,盆地里可能会积水,积水也有一个海拔高度,所有严格低于这个海拔高度的巨石,就会在水面下隐藏。

由于地壳运动,巨石的海拔高度可能会随时变化,每次一块的巨石会变成新的海拔高度。当然,水面的高度也会随时发生变化。

因为有这样奇妙的地质奇观,吸引了很多游客来游玩。uim作为一个游客,可以告诉你此时水位海拔,你得告诉他,能看到有几个连续露出水面的部分。(与水面持平我们也认为是露出)

题解

​ 考虑暴力,询问的时候 O(N) 扫一遍数组,只要相邻的两个前一个更小并且洪水的高度满足 hi1<Hhi 就直接 ans++ 。那么,我们会发现,每次修改时可能对答案产生影响的地方只可能是与修改的地方相邻的土地。并且,在 (hi1,hi] 这段区间内的询问都会加一,这是典型的区间加和单点询问,可以用树状数组或是线段树,同时不要忘记在修改修改前要先把当前位置在修改以前对答案的贡献消掉。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 400006
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    int sum=0;char ch=nc();
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
struct data{
    int l,r,tag,num;
    void add(int x){tag+=x;num+=x;}
}tree[maxn*4];
struct init{
    int k,x,y,yy,id;
    bool operator <(const init&b)const{return id<b.id;}
}a[maxn*2];
int n,tet;
void build(int p,int l,int r){
    tree[p].l=l;tree[p].r=r;
    if(l>=r)return;
    int mid=(l+r)>>1;
    build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
void pushdown(int p){
    if(!tree[p].tag)return;
    tree[p<<1].add(tree[p].tag);
    tree[p<<1|1].add(tree[p].tag);
    tree[p].tag=0;
}
void update(int p,int l,int r,int x){
    if(tree[p].l>r||tree[p].r<l)return;
    if(l<=tree[p].l&&r>=tree[p].r){
        tree[p].add(x);return;
    }
    pushdown(p);
    update(p<<1,l,r,x);update(p<<1|1,l,r,x);
}
int query(int p,int x){
    if(tree[p].l>x||tree[p].r<x)return 0;
    if(tree[p].l==tree[p].r)return tree[p].num;
    pushdown(p);
    return query(p<<1,x)+query(p<<1|1,x);
}
bool cmp(init x,init y){return x.y<y.y;}
int main(){
    n=_read();tet=_read();
    for(int i=1;i<=n;i++)a[i].y=_read(),a[i].id=i;
    for(int i=n+1;i<=n+tet;i++){
        a[i].k=_read();
        if(a[i].k==2)a[i].x=_read(),a[i].y=_read();
                else a[i].y=_read();
        a[i].id=i;
    }
    sort(a+1,a+1+n+tet,cmp);a[1].yy=1;
    for(int i=2;i<=tet+n;i++) if(a[i].y!=a[i-1].y)a[i].yy=a[i-1].yy+1;
                                           else a[i].yy=a[i-1].yy;
    build(1,1,a[tet+n].yy);
    sort(a+1,a+1+tet+n);a[0].yy=0;
    for(int i=1;i<=n;i++) if(a[i-1].yy<a[i].yy)update(1,a[i-1].yy+1,a[i].yy,1);
    for(int i=n+1;i<=n+tet;i++){
        if(a[i].k==2){
            int x=a[i].x;
            if(a[x-1].yy<a[x].yy)update(1,a[x-1].yy+1,a[x].yy,-1);
            if(x!=n&&a[x].yy<a[x+1].yy)update(1,a[x].yy+1,a[x+1].yy,-1);
            a[x].yy=a[i].yy;
            if(a[x-1].yy<a[x].yy)update(1,a[x-1].yy+1,a[x].yy,1);
            if(x!=n&&a[x].yy<a[x+1].yy)update(1,a[x].yy+1,a[x+1].yy,1);
        }else printf("%d\n",query(1,a[i].yy));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值